Compare commits
2 Commits
tach-typog
...
content-su
| Author | SHA1 | Date | |
|---|---|---|---|
| d879af2881 | |||
| cacbc016ec |
@@ -9,7 +9,10 @@ Cross-framework content text/title renderer with support for mentions, tags and
|
||||
- `ContentText`
|
||||
- `ContentTextWithSuggestions`
|
||||
- `ContentTitleWithSuggestions`
|
||||
- Angular adapter in `angular` for rendering/tokenization integration.
|
||||
- Angular UI components in `angular` (based on `@hublib-web/tach-typography/angular`):
|
||||
- `ContentTextComponent`
|
||||
- `ContentTextWithSuggestionsComponent`
|
||||
- `ContentTitleWithSuggestionsComponent`
|
||||
- Depends on `@hublib-web/tach-typography` for visual consistency.
|
||||
- Business logic (API requests for mentions/tags) stays in consumer application.
|
||||
|
||||
@@ -19,7 +22,7 @@ Cross-framework content text/title renderer with support for mentions, tags and
|
||||
yarn add "@hublib-web/content-suggestions@git+ssh://git@github.com/ORG/REPO.git#workspace=@hublib-web/content-suggestions&tag=content-suggestions-v0.1.0"
|
||||
```
|
||||
|
||||
`@hublib-web/tach-typography` is a peer dependency, so install a compatible tag for it as well.
|
||||
`@hublib-web/tach-typography@0.3.0` is a peer dependency.
|
||||
|
||||
## Install inside this monorepo
|
||||
|
||||
@@ -77,6 +80,41 @@ By default, tags are rendered as plain styled text (not links).
|
||||
|
||||
## Angular usage
|
||||
|
||||
Standalone component usage:
|
||||
|
||||
```ts
|
||||
import { Component } from "@angular/core";
|
||||
import { ContentTextWithSuggestionsComponent } from "@hublib-web/content-suggestions/angular";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [ContentTextWithSuggestionsComponent],
|
||||
template: `
|
||||
<content-text-with-suggestions
|
||||
[text]="text"
|
||||
[ellipsis]="{ count: 180, expandable: true }"
|
||||
[onView]="onView"
|
||||
/>
|
||||
`,
|
||||
})
|
||||
export class FeedPostComponent {
|
||||
text = "@[John Doe](d290f1ee-6c54-4b01-90e6-d701748f0851) hello #tag";
|
||||
|
||||
onView = () => {
|
||||
// analytics
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Title variant with defaults (`weight: "bold"`, `ellipsis: { rows: 2 }`):
|
||||
|
||||
```ts
|
||||
import { ContentTitleWithSuggestionsComponent } from "@hublib-web/content-suggestions/angular";
|
||||
|
||||
// template
|
||||
// <content-title-with-suggestions [text]="title" />
|
||||
```
|
||||
|
||||
Tokenization helper:
|
||||
|
||||
```ts
|
||||
@@ -85,32 +123,6 @@ import { createAngularContentTokens } from "@hublib-web/content-suggestions/angu
|
||||
const tokens = createAngularContentTokens(text);
|
||||
```
|
||||
|
||||
Ready-to-use UI renderer (React-like props API):
|
||||
|
||||
```ts
|
||||
import { AngularContentTextWithSuggestionsRenderer } from "@hublib-web/content-suggestions/angular";
|
||||
|
||||
const renderer = new AngularContentTextWithSuggestionsRenderer();
|
||||
renderer.attach(containerElement, {
|
||||
text,
|
||||
weight: "normal",
|
||||
ellipsis: { count: 180, expandable: true },
|
||||
blur: false,
|
||||
onView: () => {
|
||||
// analytics
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Title variant with defaults (`weight: "bold"`, `ellipsis: { rows: 2 }`):
|
||||
|
||||
```ts
|
||||
import { AngularContentTitleWithSuggestionsRenderer } from "@hublib-web/content-suggestions/angular";
|
||||
|
||||
const titleRenderer = new AngularContentTitleWithSuggestionsRenderer();
|
||||
titleRenderer.attach(titleContainer, { text: title });
|
||||
```
|
||||
|
||||
## Storybook (dev/design system)
|
||||
|
||||
Run from repository root:
|
||||
|
||||
21
packages/content-suggestions/dist/angular/angular/content-text-with-suggestions.component.d.ts
vendored
Normal file
21
packages/content-suggestions/dist/angular/angular/content-text-with-suggestions.component.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { AngularContentEllipsisConfig, AngularLinkRenderer, AngularMentionRenderer, AngularTagRenderer } from "./types";
|
||||
import { EventEmitter } from "@angular/core";
|
||||
import type { TypographyWeight } from "@hublib-web/tach-typography/core";
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class ContentTextWithSuggestionsComponent {
|
||||
className: string | undefined;
|
||||
weight: TypographyWeight;
|
||||
text: string | null | undefined;
|
||||
ellipsis: AngularContentEllipsisConfig;
|
||||
blur: boolean;
|
||||
style: Record<string, string | number> | null | undefined;
|
||||
onView: (() => void) | undefined;
|
||||
renderMention: AngularMentionRenderer | undefined;
|
||||
renderTag: AngularTagRenderer | undefined;
|
||||
renderLink: AngularLinkRenderer | undefined;
|
||||
readonly viewed: EventEmitter<void>;
|
||||
readonly expandedChange: EventEmitter<boolean>;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<ContentTextWithSuggestionsComponent, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<ContentTextWithSuggestionsComponent, "content-text-with-suggestions", never, { "className": { "alias": "className"; "required": false; }; "weight": { "alias": "weight"; "required": false; }; "text": { "alias": "text"; "required": false; }; "ellipsis": { "alias": "ellipsis"; "required": false; }; "blur": { "alias": "blur"; "required": false; }; "style": { "alias": "style"; "required": false; }; "onView": { "alias": "onView"; "required": false; }; "renderMention": { "alias": "renderMention"; "required": false; }; "renderTag": { "alias": "renderTag"; "required": false; }; "renderLink": { "alias": "renderLink"; "required": false; }; }, { "viewed": "viewed"; "expandedChange": "expandedChange"; }, never, never, true, never>;
|
||||
}
|
||||
//# sourceMappingURL=content-text-with-suggestions.component.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"content-text-with-suggestions.component.d.ts","sourceRoot":"","sources":["../../../src/angular/content-text-with-suggestions.component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,4BAA4B,EAC5B,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAsC,YAAY,EAAiB,MAAM,eAAe,CAAC;AAEhG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;;AAIzE,qBAsBa,mCAAmC;IACrC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,gBAAgB,CAAY;IACpC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,4BAA4B,CAAS;IAC/C,IAAI,UAAS;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1D,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAClD,SAAS,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC1C,UAAU,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAE3C,QAAQ,CAAC,MAAM,qBAA4B;IAC3C,QAAQ,CAAC,cAAc,wBAA+B;yCAbrD,mCAAmC;2CAAnC,mCAAmC;CAc/C"}
|
||||
84
packages/content-suggestions/dist/angular/angular/content-text-with-suggestions.component.js
vendored
Normal file
84
packages/content-suggestions/dist/angular/angular/content-text-with-suggestions.component.js
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
import { ContentTextComponent } from "./content-text.component";
|
||||
import * as i0 from "@angular/core";
|
||||
export class ContentTextWithSuggestionsComponent {
|
||||
className;
|
||||
weight = "normal";
|
||||
text;
|
||||
ellipsis = false;
|
||||
blur = false;
|
||||
style;
|
||||
onView;
|
||||
renderMention;
|
||||
renderTag;
|
||||
renderLink;
|
||||
viewed = new EventEmitter();
|
||||
expandedChange = new EventEmitter();
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentTextWithSuggestionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: ContentTextWithSuggestionsComponent, isStandalone: true, selector: "content-text-with-suggestions", inputs: { className: "className", weight: "weight", text: "text", ellipsis: "ellipsis", blur: "blur", style: "style", onView: "onView", renderMention: "renderMention", renderTag: "renderTag", renderLink: "renderLink" }, outputs: { viewed: "viewed", expandedChange: "expandedChange" }, ngImport: i0, template: `
|
||||
<content-text
|
||||
[className]="className"
|
||||
[weight]="weight"
|
||||
[text]="text"
|
||||
[ellipsis]="ellipsis"
|
||||
[blur]="blur"
|
||||
[style]="style"
|
||||
[onView]="onView"
|
||||
[renderMention]="renderMention"
|
||||
[renderTag]="renderTag"
|
||||
[renderLink]="renderLink"
|
||||
(viewed)="viewed.emit()"
|
||||
(expandedChange)="expandedChange.emit($event)"
|
||||
/>
|
||||
`, isInline: true, dependencies: [{ kind: "component", type: ContentTextComponent, selector: "content-text", inputs: ["className", "weight", "text", "ellipsis", "blur", "style", "onView", "renderMention", "renderTag", "renderLink"], outputs: ["viewed", "expandedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentTextWithSuggestionsComponent, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
selector: "content-text-with-suggestions",
|
||||
standalone: true,
|
||||
imports: [ContentTextComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<content-text
|
||||
[className]="className"
|
||||
[weight]="weight"
|
||||
[text]="text"
|
||||
[ellipsis]="ellipsis"
|
||||
[blur]="blur"
|
||||
[style]="style"
|
||||
[onView]="onView"
|
||||
[renderMention]="renderMention"
|
||||
[renderTag]="renderTag"
|
||||
[renderLink]="renderLink"
|
||||
(viewed)="viewed.emit()"
|
||||
(expandedChange)="expandedChange.emit($event)"
|
||||
/>
|
||||
`,
|
||||
}]
|
||||
}], propDecorators: { className: [{
|
||||
type: Input
|
||||
}], weight: [{
|
||||
type: Input
|
||||
}], text: [{
|
||||
type: Input
|
||||
}], ellipsis: [{
|
||||
type: Input
|
||||
}], blur: [{
|
||||
type: Input
|
||||
}], style: [{
|
||||
type: Input
|
||||
}], onView: [{
|
||||
type: Input
|
||||
}], renderMention: [{
|
||||
type: Input
|
||||
}], renderTag: [{
|
||||
type: Input
|
||||
}], renderLink: [{
|
||||
type: Input
|
||||
}], viewed: [{
|
||||
type: Output
|
||||
}], expandedChange: [{
|
||||
type: Output
|
||||
}] } });
|
||||
//# sourceMappingURL=content-text-with-suggestions.component.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"content-text-with-suggestions.component.js","sourceRoot":"","sources":["../../../src/angular/content-text-with-suggestions.component.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAIhG,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;;AAwBhE,MAAM,OAAO,mCAAmC;IACrC,SAAS,CAAqB;IAC9B,MAAM,GAAqB,QAAQ,CAAC;IACpC,IAAI,CAA4B;IAChC,QAAQ,GAAiC,KAAK,CAAC;IAC/C,IAAI,GAAG,KAAK,CAAC;IACb,KAAK,CAAqD;IAC1D,MAAM,CAA2B;IACjC,aAAa,CAAqC;IAClD,SAAS,CAAiC;IAC1C,UAAU,CAAkC;IAElC,MAAM,GAAG,IAAI,YAAY,EAAQ,CAAC;IAClC,cAAc,GAAG,IAAI,YAAY,EAAW,CAAC;wGAbrD,mCAAmC;4FAAnC,mCAAmC,sXAjBpC;;;;;;;;;;;;;;;GAeT,4DAjBS,oBAAoB;;4FAmBnB,mCAAmC;kBAtB/C,SAAS;mBAAC;oBACT,QAAQ,EAAE,+BAA+B;oBACzC,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,oBAAoB,CAAC;oBAC/B,eAAe,EAAE,uBAAuB,CAAC,MAAM;oBAC/C,QAAQ,EAAE;;;;;;;;;;;;;;;GAeT;iBACF;;sBAEE,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBAEL,MAAM;;sBACN,MAAM"}
|
||||
92
packages/content-suggestions/dist/angular/angular/content-text.component.d.ts
vendored
Normal file
92
packages/content-suggestions/dist/angular/angular/content-text.component.d.ts
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
import type { LinkEntity, MentionEntity, TagEntity } from "../core";
|
||||
import type { AngularContentEllipsisConfig, AngularLinkRenderer, AngularRenderResult, AngularTagRenderer, AngularMentionRenderer } from "./types";
|
||||
import { AfterViewInit, ChangeDetectorRef, EventEmitter, OnChanges, OnDestroy, SimpleChanges } from "@angular/core";
|
||||
import { type TachTypographyHostProps } from "@hublib-web/tach-typography/angular";
|
||||
import type { TypographyWeight } from "@hublib-web/tach-typography/core";
|
||||
import * as i0 from "@angular/core";
|
||||
type TextPart = {
|
||||
kind: "text";
|
||||
key: string;
|
||||
text: string;
|
||||
};
|
||||
type MentionPart = {
|
||||
kind: "mention";
|
||||
key: string;
|
||||
entity: MentionEntity;
|
||||
index: number;
|
||||
customRender: AngularRenderResult | null;
|
||||
};
|
||||
type TagPart = {
|
||||
kind: "tag";
|
||||
key: string;
|
||||
entity: TagEntity;
|
||||
index: number;
|
||||
customRender: AngularRenderResult | null;
|
||||
};
|
||||
type LinkPart = {
|
||||
kind: "link";
|
||||
key: string;
|
||||
entity: LinkEntity;
|
||||
index: number;
|
||||
customRender: AngularRenderResult | null;
|
||||
};
|
||||
type ContentPart = TextPart | MentionPart | TagPart | LinkPart;
|
||||
export declare class ContentTextComponent implements OnChanges, AfterViewInit, OnDestroy {
|
||||
private readonly cdr;
|
||||
className: string | undefined;
|
||||
weight: TypographyWeight;
|
||||
text: string | null | undefined;
|
||||
ellipsis: AngularContentEllipsisConfig;
|
||||
blur: boolean;
|
||||
style: Record<string, string | number> | null | undefined;
|
||||
onView: (() => void) | undefined;
|
||||
renderMention: AngularMentionRenderer | undefined;
|
||||
renderTag: AngularTagRenderer | undefined;
|
||||
renderLink: AngularLinkRenderer | undefined;
|
||||
readonly viewed: EventEmitter<void>;
|
||||
readonly expandedChange: EventEmitter<boolean>;
|
||||
private readonly containerRef;
|
||||
visibleParts: ContentPart[];
|
||||
mergedStyle: Record<string, string | number>;
|
||||
rowsEllipsis: {
|
||||
rows: number;
|
||||
} | undefined;
|
||||
showTrailingEllipsis: boolean;
|
||||
showInlineReadMore: boolean;
|
||||
showBlockReadMore: boolean;
|
||||
readMoreLabel: string;
|
||||
paragraphHostProps: TachTypographyHostProps;
|
||||
private expanded;
|
||||
private mergedExpanded;
|
||||
private isExpandedControlled;
|
||||
private isInsideView;
|
||||
private viewedFired;
|
||||
private pendingRowsOverflowCheck;
|
||||
private intersectionObserver;
|
||||
constructor(cdr: ChangeDetectorRef);
|
||||
ngOnChanges(_changes: SimpleChanges): void;
|
||||
ngAfterViewInit(): void;
|
||||
ngOnDestroy(): void;
|
||||
trackByKey(_index: number, part: ContentPart): string;
|
||||
buildDefaultLinkHostProps(url: string): TachTypographyHostProps;
|
||||
handleExpand(event: MouseEvent): void;
|
||||
private recomputeContent;
|
||||
private buildParts;
|
||||
private resolveCustomRender;
|
||||
private resolveEllipsisConfig;
|
||||
private isCountConfig;
|
||||
private isRowsConfig;
|
||||
private getMergedExpanded;
|
||||
private resolveEllipsisSymbol;
|
||||
private notifyExpand;
|
||||
private computeEntitySafeCutoff;
|
||||
private initObserver;
|
||||
private emitViewIfNeeded;
|
||||
private scheduleRowsOverflowCheck;
|
||||
private refreshRowsOverflow;
|
||||
private getParagraphElement;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<ContentTextComponent, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<ContentTextComponent, "content-text", never, { "className": { "alias": "className"; "required": false; }; "weight": { "alias": "weight"; "required": false; }; "text": { "alias": "text"; "required": false; }; "ellipsis": { "alias": "ellipsis"; "required": false; }; "blur": { "alias": "blur"; "required": false; }; "style": { "alias": "style"; "required": false; }; "onView": { "alias": "onView"; "required": false; }; "renderMention": { "alias": "renderMention"; "required": false; }; "renderTag": { "alias": "renderTag"; "required": false; }; "renderLink": { "alias": "renderLink"; "required": false; }; }, { "viewed": "viewed"; "expandedChange": "expandedChange"; }, never, never, true, never>;
|
||||
}
|
||||
export {};
|
||||
//# sourceMappingURL=content-text.component.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/angular/content-text.component.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/content-text.component.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"content-text.component.d.ts","sourceRoot":"","sources":["../../../src/angular/content-text.component.ts"],"names":[],"mappings":"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"}
|
||||
536
packages/content-suggestions/dist/angular/angular/content-text.component.js
vendored
Normal file
536
packages/content-suggestions/dist/angular/angular/content-text.component.js
vendored
Normal file
@@ -0,0 +1,536 @@
|
||||
import { NgFor, NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault } from "@angular/common";
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewChild, } from "@angular/core";
|
||||
import { TachTypographyComponent, } from "@hublib-web/tach-typography/angular";
|
||||
import { findAllEntities } from "../core";
|
||||
import { ContentSuggestionsRenderResultDirective } from "./render-result.directive";
|
||||
import * as i0 from "@angular/core";
|
||||
const READ_MORE_TEXT = "Читать полностью";
|
||||
const BODY_DATA_ATTR = "data-content-suggestions-body";
|
||||
const BASE_SELECTABLE_STYLE = {
|
||||
whiteSpace: "pre-wrap",
|
||||
WebkitTouchCallout: "default",
|
||||
WebkitUserSelect: "text",
|
||||
KhtmlUserSelect: "text",
|
||||
MozUserSelect: "text",
|
||||
msUserSelect: "text",
|
||||
userSelect: "text",
|
||||
};
|
||||
const BLUR_STYLE = {
|
||||
filter: "blur(3px)",
|
||||
WebkitUserSelect: "none",
|
||||
KhtmlUserSelect: "none",
|
||||
MozUserSelect: "none",
|
||||
msUserSelect: "none",
|
||||
userSelect: "none",
|
||||
pointerEvents: "none",
|
||||
};
|
||||
export class ContentTextComponent {
|
||||
cdr;
|
||||
className;
|
||||
weight = "normal";
|
||||
text;
|
||||
ellipsis = false;
|
||||
blur = false;
|
||||
style;
|
||||
onView;
|
||||
renderMention;
|
||||
renderTag;
|
||||
renderLink;
|
||||
viewed = new EventEmitter();
|
||||
expandedChange = new EventEmitter();
|
||||
containerRef;
|
||||
visibleParts = [];
|
||||
mergedStyle = { ...BASE_SELECTABLE_STYLE };
|
||||
rowsEllipsis;
|
||||
showTrailingEllipsis = false;
|
||||
showInlineReadMore = false;
|
||||
showBlockReadMore = false;
|
||||
readMoreLabel = READ_MORE_TEXT;
|
||||
paragraphHostProps = { [BODY_DATA_ATTR]: "true" };
|
||||
expanded = false;
|
||||
mergedExpanded = false;
|
||||
isExpandedControlled = false;
|
||||
isInsideView = false;
|
||||
viewedFired = false;
|
||||
pendingRowsOverflowCheck = false;
|
||||
intersectionObserver = null;
|
||||
constructor(cdr) {
|
||||
this.cdr = cdr;
|
||||
}
|
||||
ngOnChanges(_changes) {
|
||||
this.recomputeContent();
|
||||
}
|
||||
ngAfterViewInit() {
|
||||
this.initObserver();
|
||||
this.scheduleRowsOverflowCheck();
|
||||
}
|
||||
ngOnDestroy() {
|
||||
if (this.intersectionObserver) {
|
||||
this.intersectionObserver.disconnect();
|
||||
this.intersectionObserver = null;
|
||||
}
|
||||
}
|
||||
trackByKey(_index, part) {
|
||||
return part.key;
|
||||
}
|
||||
buildDefaultLinkHostProps(url) {
|
||||
return {
|
||||
href: url,
|
||||
target: "_blank",
|
||||
referrerPolicy: "no-referrer",
|
||||
};
|
||||
}
|
||||
handleExpand(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!this.isExpandedControlled) {
|
||||
this.expanded = true;
|
||||
}
|
||||
this.notifyExpand(event, true);
|
||||
this.recomputeContent();
|
||||
}
|
||||
recomputeContent() {
|
||||
const content = this.text ?? "";
|
||||
const entities = findAllEntities(content);
|
||||
const ellipsisConfig = this.resolveEllipsisConfig();
|
||||
this.isExpandedControlled = Boolean(ellipsisConfig && typeof ellipsisConfig.expanded === "boolean");
|
||||
if (!this.isExpandedControlled && this.isCountConfig(ellipsisConfig)) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
if (content.length <= count && !this.expanded) {
|
||||
this.expanded = true;
|
||||
}
|
||||
}
|
||||
this.mergedExpanded = this.getMergedExpanded(ellipsisConfig, content);
|
||||
this.readMoreLabel = this.resolveEllipsisSymbol(ellipsisConfig?.symbol, this.mergedExpanded);
|
||||
this.mergedStyle = {
|
||||
...BASE_SELECTABLE_STYLE,
|
||||
...(this.blur ? BLUR_STYLE : {}),
|
||||
...(this.style ?? {}),
|
||||
};
|
||||
this.visibleParts = this.buildParts(content, entities, null);
|
||||
this.rowsEllipsis = undefined;
|
||||
this.showTrailingEllipsis = false;
|
||||
this.showInlineReadMore = false;
|
||||
this.showBlockReadMore = false;
|
||||
this.pendingRowsOverflowCheck = false;
|
||||
if (this.isCountConfig(ellipsisConfig)) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
const shouldTruncate = !this.mergedExpanded && count > 0 && content.length > count;
|
||||
if (shouldTruncate) {
|
||||
const cutoff = this.computeEntitySafeCutoff(count, entities);
|
||||
this.visibleParts = this.buildParts(content, entities, cutoff);
|
||||
this.showTrailingEllipsis = true;
|
||||
this.showInlineReadMore = Boolean(ellipsisConfig.expandable);
|
||||
}
|
||||
}
|
||||
else if (this.isRowsConfig(ellipsisConfig) && !this.mergedExpanded) {
|
||||
this.rowsEllipsis = {
|
||||
rows: ellipsisConfig.rows,
|
||||
};
|
||||
if (ellipsisConfig.expandable) {
|
||||
this.pendingRowsOverflowCheck = true;
|
||||
}
|
||||
}
|
||||
this.emitViewIfNeeded();
|
||||
this.scheduleRowsOverflowCheck();
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
buildParts(content, entities, upto) {
|
||||
const parts = [];
|
||||
let lastIndex = 0;
|
||||
for (const [index, entity] of entities.entries()) {
|
||||
if (upto !== null && entity.start >= upto) {
|
||||
break;
|
||||
}
|
||||
const textEnd = upto !== null ? Math.min(entity.start, upto) : entity.start;
|
||||
if (entity.start > lastIndex && lastIndex < textEnd) {
|
||||
parts.push({
|
||||
kind: "text",
|
||||
key: `text-${lastIndex}-${textEnd}`,
|
||||
text: content.slice(lastIndex, textEnd),
|
||||
});
|
||||
}
|
||||
if (upto === null || entity.end <= upto) {
|
||||
if (entity.type === "mention") {
|
||||
parts.push({
|
||||
kind: "mention",
|
||||
key: `mention-${entity.start}-${index}`,
|
||||
entity,
|
||||
index,
|
||||
customRender: this.resolveCustomRender(this.renderMention, entity, index),
|
||||
});
|
||||
}
|
||||
else if (entity.type === "tag") {
|
||||
parts.push({
|
||||
kind: "tag",
|
||||
key: `tag-${entity.start}-${index}`,
|
||||
entity,
|
||||
index,
|
||||
customRender: this.resolveCustomRender(this.renderTag, entity, index),
|
||||
});
|
||||
}
|
||||
else {
|
||||
parts.push({
|
||||
kind: "link",
|
||||
key: `link-${entity.start}-${index}`,
|
||||
entity,
|
||||
index,
|
||||
customRender: this.resolveCustomRender(this.renderLink, entity, index),
|
||||
});
|
||||
}
|
||||
}
|
||||
lastIndex = entity.end;
|
||||
}
|
||||
if (upto === null) {
|
||||
if (lastIndex < content.length) {
|
||||
parts.push({
|
||||
kind: "text",
|
||||
key: `text-${lastIndex}-${content.length}`,
|
||||
text: content.slice(lastIndex),
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (lastIndex < upto) {
|
||||
parts.push({
|
||||
kind: "text",
|
||||
key: `text-${lastIndex}-${upto}`,
|
||||
text: content.slice(lastIndex, upto),
|
||||
});
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
resolveCustomRender(rendererFn, entity, index) {
|
||||
const rendered = rendererFn?.(entity, index);
|
||||
return rendered ?? null;
|
||||
}
|
||||
resolveEllipsisConfig() {
|
||||
if (!this.ellipsis || typeof this.ellipsis !== "object") {
|
||||
return null;
|
||||
}
|
||||
return this.ellipsis;
|
||||
}
|
||||
isCountConfig(config) {
|
||||
return Boolean(config && "count" in config);
|
||||
}
|
||||
isRowsConfig(config) {
|
||||
return Boolean(config && "rows" in config);
|
||||
}
|
||||
getMergedExpanded(config, text) {
|
||||
const controlledExpanded = config && typeof config.expanded === "boolean"
|
||||
? config.expanded
|
||||
: undefined;
|
||||
if (controlledExpanded !== undefined) {
|
||||
return controlledExpanded;
|
||||
}
|
||||
if (this.isCountConfig(config)) {
|
||||
const count = config.count ?? 0;
|
||||
if (count <= 0 || text.length <= count) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return this.expanded;
|
||||
}
|
||||
resolveEllipsisSymbol(symbol, expanded) {
|
||||
if (typeof symbol === "function") {
|
||||
return symbol(expanded);
|
||||
}
|
||||
return symbol ?? READ_MORE_TEXT;
|
||||
}
|
||||
notifyExpand(event, expanded) {
|
||||
const config = this.resolveEllipsisConfig();
|
||||
const onExpand = config?.onExpand;
|
||||
if (onExpand) {
|
||||
if (onExpand.length >= 2) {
|
||||
onExpand(event, { expanded });
|
||||
}
|
||||
else {
|
||||
onExpand(expanded);
|
||||
}
|
||||
}
|
||||
this.expandedChange.emit(expanded);
|
||||
}
|
||||
computeEntitySafeCutoff(count, entities) {
|
||||
let cutoff = count;
|
||||
let extended = true;
|
||||
while (extended) {
|
||||
extended = false;
|
||||
for (const entity of entities) {
|
||||
if (entity.start < cutoff && entity.end > cutoff) {
|
||||
cutoff = entity.end;
|
||||
extended = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cutoff;
|
||||
}
|
||||
initObserver() {
|
||||
const host = this.containerRef?.nativeElement;
|
||||
if (!host) {
|
||||
return;
|
||||
}
|
||||
if (typeof IntersectionObserver === "undefined") {
|
||||
this.isInsideView = true;
|
||||
this.emitViewIfNeeded();
|
||||
return;
|
||||
}
|
||||
this.intersectionObserver = new IntersectionObserver(entries => {
|
||||
const entry = entries[0];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
if (entry.isIntersecting && !this.isInsideView) {
|
||||
this.isInsideView = true;
|
||||
this.emitViewIfNeeded();
|
||||
}
|
||||
}, { threshold: 0.5 });
|
||||
this.intersectionObserver.observe(host);
|
||||
}
|
||||
emitViewIfNeeded() {
|
||||
if (!this.mergedExpanded || this.viewedFired || !this.isInsideView) {
|
||||
return;
|
||||
}
|
||||
this.viewedFired = true;
|
||||
this.onView?.();
|
||||
this.viewed.emit();
|
||||
}
|
||||
scheduleRowsOverflowCheck() {
|
||||
if (!this.pendingRowsOverflowCheck) {
|
||||
return;
|
||||
}
|
||||
queueMicrotask(() => {
|
||||
this.refreshRowsOverflow();
|
||||
});
|
||||
}
|
||||
refreshRowsOverflow() {
|
||||
const paragraph = this.getParagraphElement();
|
||||
if (!paragraph) {
|
||||
return;
|
||||
}
|
||||
const hasOverflow = paragraph.scrollHeight - paragraph.clientHeight > 1;
|
||||
if (this.showBlockReadMore !== hasOverflow) {
|
||||
this.showBlockReadMore = hasOverflow;
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
}
|
||||
getParagraphElement() {
|
||||
const host = this.containerRef?.nativeElement;
|
||||
if (!host) {
|
||||
return null;
|
||||
}
|
||||
return host.querySelector(`[${BODY_DATA_ATTR}="true"]`);
|
||||
}
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentTextComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
||||
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: ContentTextComponent, isStandalone: true, selector: "content-text", inputs: { className: "className", weight: "weight", text: "text", ellipsis: "ellipsis", blur: "blur", style: "style", onView: "onView", renderMention: "renderMention", renderTag: "renderTag", renderLink: "renderLink" }, outputs: { viewed: "viewed", expandedChange: "expandedChange" }, viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
|
||||
<div #container>
|
||||
<tach-typography
|
||||
as="p"
|
||||
variant="Body"
|
||||
[weight]="weight"
|
||||
[className]="className"
|
||||
[preserveStyle]="mergedStyle"
|
||||
[ellipsis]="rowsEllipsis"
|
||||
[hostProps]="paragraphHostProps"
|
||||
>
|
||||
<ng-container *ngFor="let part of visibleParts; trackBy: trackByKey">
|
||||
<ng-container [ngSwitch]="part.kind">
|
||||
<ng-container *ngSwitchCase="'text'">{{ part.text }}</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'mention'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultMention">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultMention>
|
||||
<tach-typography as="span" variant="Body" color="link" [weight]="weight">
|
||||
{{ part.entity.displayText }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'tag'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultTag">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultTag>
|
||||
<tach-typography as="span" variant="Body" color="link" [weight]="weight">
|
||||
{{ part.entity.text }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'link'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultLink">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultLink>
|
||||
<tach-typography
|
||||
as="a"
|
||||
variant="Body"
|
||||
color="link"
|
||||
[weight]="weight"
|
||||
[hostProps]="buildDefaultLinkHostProps(part.entity.url)"
|
||||
>
|
||||
{{ part.entity.text }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchDefault></ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="showTrailingEllipsis">…</ng-container>
|
||||
|
||||
<button
|
||||
*ngIf="showInlineReadMore"
|
||||
type="button"
|
||||
(click)="handleExpand($event)"
|
||||
style="border: 0; background: none; padding: 0; margin-left: 6px; cursor: pointer;"
|
||||
>
|
||||
<tach-typography as="span" variant="Body" color="link" weight="bold">
|
||||
{{ readMoreLabel }}
|
||||
</tach-typography>
|
||||
</button>
|
||||
</tach-typography>
|
||||
|
||||
<button
|
||||
*ngIf="showBlockReadMore"
|
||||
type="button"
|
||||
(click)="handleExpand($event)"
|
||||
style="border: 0; background: none; padding: 0; margin-top: 6px; cursor: pointer;"
|
||||
>
|
||||
<tach-typography as="span" variant="Body" color="link" weight="bold">
|
||||
{{ readMoreLabel }}
|
||||
</tach-typography>
|
||||
</button>
|
||||
</div>
|
||||
`, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: TachTypographyComponent, selector: "tach-typography", inputs: ["as", "variant", "color", "weight", "clickable", "markdownEnabled", "markdown", "className", "ellipsis", "nzProps", "hostProps", "preserveStyle"], outputs: ["tachClick"] }, { kind: "directive", type: ContentSuggestionsRenderResultDirective, selector: "[contentSuggestionsRenderResult]", inputs: ["contentSuggestionsRenderResult"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentTextComponent, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
selector: "content-text",
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgFor,
|
||||
NgIf,
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
NgSwitchDefault,
|
||||
TachTypographyComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<div #container>
|
||||
<tach-typography
|
||||
as="p"
|
||||
variant="Body"
|
||||
[weight]="weight"
|
||||
[className]="className"
|
||||
[preserveStyle]="mergedStyle"
|
||||
[ellipsis]="rowsEllipsis"
|
||||
[hostProps]="paragraphHostProps"
|
||||
>
|
||||
<ng-container *ngFor="let part of visibleParts; trackBy: trackByKey">
|
||||
<ng-container [ngSwitch]="part.kind">
|
||||
<ng-container *ngSwitchCase="'text'">{{ part.text }}</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'mention'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultMention">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultMention>
|
||||
<tach-typography as="span" variant="Body" color="link" [weight]="weight">
|
||||
{{ part.entity.displayText }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'tag'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultTag">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultTag>
|
||||
<tach-typography as="span" variant="Body" color="link" [weight]="weight">
|
||||
{{ part.entity.text }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'link'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultLink">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultLink>
|
||||
<tach-typography
|
||||
as="a"
|
||||
variant="Body"
|
||||
color="link"
|
||||
[weight]="weight"
|
||||
[hostProps]="buildDefaultLinkHostProps(part.entity.url)"
|
||||
>
|
||||
{{ part.entity.text }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchDefault></ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="showTrailingEllipsis">…</ng-container>
|
||||
|
||||
<button
|
||||
*ngIf="showInlineReadMore"
|
||||
type="button"
|
||||
(click)="handleExpand($event)"
|
||||
style="border: 0; background: none; padding: 0; margin-left: 6px; cursor: pointer;"
|
||||
>
|
||||
<tach-typography as="span" variant="Body" color="link" weight="bold">
|
||||
{{ readMoreLabel }}
|
||||
</tach-typography>
|
||||
</button>
|
||||
</tach-typography>
|
||||
|
||||
<button
|
||||
*ngIf="showBlockReadMore"
|
||||
type="button"
|
||||
(click)="handleExpand($event)"
|
||||
style="border: 0; background: none; padding: 0; margin-top: 6px; cursor: pointer;"
|
||||
>
|
||||
<tach-typography as="span" variant="Body" color="link" weight="bold">
|
||||
{{ readMoreLabel }}
|
||||
</tach-typography>
|
||||
</button>
|
||||
</div>
|
||||
`,
|
||||
}]
|
||||
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { className: [{
|
||||
type: Input
|
||||
}], weight: [{
|
||||
type: Input
|
||||
}], text: [{
|
||||
type: Input
|
||||
}], ellipsis: [{
|
||||
type: Input
|
||||
}], blur: [{
|
||||
type: Input
|
||||
}], style: [{
|
||||
type: Input
|
||||
}], onView: [{
|
||||
type: Input
|
||||
}], renderMention: [{
|
||||
type: Input
|
||||
}], renderTag: [{
|
||||
type: Input
|
||||
}], renderLink: [{
|
||||
type: Input
|
||||
}], viewed: [{
|
||||
type: Output
|
||||
}], expandedChange: [{
|
||||
type: Output
|
||||
}], containerRef: [{
|
||||
type: ViewChild,
|
||||
args: ["container", { static: true }]
|
||||
}] } });
|
||||
//# sourceMappingURL=content-text.component.js.map
|
||||
1
packages/content-suggestions/dist/angular/angular/content-text.component.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/content-text.component.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
20
packages/content-suggestions/dist/angular/angular/content-title-with-suggestions.component.d.ts
vendored
Normal file
20
packages/content-suggestions/dist/angular/angular/content-title-with-suggestions.component.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { AngularContentEllipsisConfig, AngularLinkRenderer, AngularMentionRenderer, AngularTagRenderer } from "./types";
|
||||
import { EventEmitter } from "@angular/core";
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class ContentTitleWithSuggestionsComponent {
|
||||
className: string | undefined;
|
||||
text: string | null | undefined;
|
||||
ellipsis: AngularContentEllipsisConfig | undefined;
|
||||
blur: boolean;
|
||||
style: Record<string, string | number> | null | undefined;
|
||||
onView: (() => void) | undefined;
|
||||
renderMention: AngularMentionRenderer | undefined;
|
||||
renderTag: AngularTagRenderer | undefined;
|
||||
renderLink: AngularLinkRenderer | undefined;
|
||||
readonly viewed: EventEmitter<void>;
|
||||
readonly expandedChange: EventEmitter<boolean>;
|
||||
get normalizedEllipsis(): AngularContentEllipsisConfig;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<ContentTitleWithSuggestionsComponent, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<ContentTitleWithSuggestionsComponent, "content-title-with-suggestions", never, { "className": { "alias": "className"; "required": false; }; "text": { "alias": "text"; "required": false; }; "ellipsis": { "alias": "ellipsis"; "required": false; }; "blur": { "alias": "blur"; "required": false; }; "style": { "alias": "style"; "required": false; }; "onView": { "alias": "onView"; "required": false; }; "renderMention": { "alias": "renderMention"; "required": false; }; "renderTag": { "alias": "renderTag"; "required": false; }; "renderLink": { "alias": "renderLink"; "required": false; }; }, { "viewed": "viewed"; "expandedChange": "expandedChange"; }, never, never, true, never>;
|
||||
}
|
||||
//# sourceMappingURL=content-title-with-suggestions.component.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"content-title-with-suggestions.component.d.ts","sourceRoot":"","sources":["../../../src/angular/content-title-with-suggestions.component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,4BAA4B,EAC5B,mBAAmB,EACnB,sBAAsB,EAEtB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAsC,YAAY,EAAiB,MAAM,eAAe,CAAC;;AAIhG,qBAsBa,oCAAoC;IACtC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,4BAA4B,GAAG,SAAS,CAAC;IACnD,IAAI,UAAS;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1D,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAClD,SAAS,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC1C,UAAU,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAE3C,QAAQ,CAAC,MAAM,qBAA4B;IAC3C,QAAQ,CAAC,cAAc,wBAA+B;IAEhE,IAAI,kBAAkB,IAAI,4BAA4B,CAMrD;yCApBU,oCAAoC;2CAApC,oCAAoC;CAqBhD"}
|
||||
87
packages/content-suggestions/dist/angular/angular/content-title-with-suggestions.component.js
vendored
Normal file
87
packages/content-suggestions/dist/angular/angular/content-title-with-suggestions.component.js
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
import { ContentTextWithSuggestionsComponent } from "./content-text-with-suggestions.component";
|
||||
import * as i0 from "@angular/core";
|
||||
export class ContentTitleWithSuggestionsComponent {
|
||||
className;
|
||||
text;
|
||||
ellipsis;
|
||||
blur = false;
|
||||
style;
|
||||
onView;
|
||||
renderMention;
|
||||
renderTag;
|
||||
renderLink;
|
||||
viewed = new EventEmitter();
|
||||
expandedChange = new EventEmitter();
|
||||
get normalizedEllipsis() {
|
||||
if (this.ellipsis !== undefined) {
|
||||
return this.ellipsis;
|
||||
}
|
||||
return { rows: 2 };
|
||||
}
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentTitleWithSuggestionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: ContentTitleWithSuggestionsComponent, isStandalone: true, selector: "content-title-with-suggestions", inputs: { className: "className", text: "text", ellipsis: "ellipsis", blur: "blur", style: "style", onView: "onView", renderMention: "renderMention", renderTag: "renderTag", renderLink: "renderLink" }, outputs: { viewed: "viewed", expandedChange: "expandedChange" }, ngImport: i0, template: `
|
||||
<content-text-with-suggestions
|
||||
[className]="className"
|
||||
weight="bold"
|
||||
[text]="text"
|
||||
[ellipsis]="normalizedEllipsis"
|
||||
[blur]="blur"
|
||||
[style]="style"
|
||||
[onView]="onView"
|
||||
[renderMention]="renderMention"
|
||||
[renderTag]="renderTag"
|
||||
[renderLink]="renderLink"
|
||||
(viewed)="viewed.emit()"
|
||||
(expandedChange)="expandedChange.emit($event)"
|
||||
/>
|
||||
`, isInline: true, dependencies: [{ kind: "component", type: ContentTextWithSuggestionsComponent, selector: "content-text-with-suggestions", inputs: ["className", "weight", "text", "ellipsis", "blur", "style", "onView", "renderMention", "renderTag", "renderLink"], outputs: ["viewed", "expandedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentTitleWithSuggestionsComponent, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
selector: "content-title-with-suggestions",
|
||||
standalone: true,
|
||||
imports: [ContentTextWithSuggestionsComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<content-text-with-suggestions
|
||||
[className]="className"
|
||||
weight="bold"
|
||||
[text]="text"
|
||||
[ellipsis]="normalizedEllipsis"
|
||||
[blur]="blur"
|
||||
[style]="style"
|
||||
[onView]="onView"
|
||||
[renderMention]="renderMention"
|
||||
[renderTag]="renderTag"
|
||||
[renderLink]="renderLink"
|
||||
(viewed)="viewed.emit()"
|
||||
(expandedChange)="expandedChange.emit($event)"
|
||||
/>
|
||||
`,
|
||||
}]
|
||||
}], propDecorators: { className: [{
|
||||
type: Input
|
||||
}], text: [{
|
||||
type: Input
|
||||
}], ellipsis: [{
|
||||
type: Input
|
||||
}], blur: [{
|
||||
type: Input
|
||||
}], style: [{
|
||||
type: Input
|
||||
}], onView: [{
|
||||
type: Input
|
||||
}], renderMention: [{
|
||||
type: Input
|
||||
}], renderTag: [{
|
||||
type: Input
|
||||
}], renderLink: [{
|
||||
type: Input
|
||||
}], viewed: [{
|
||||
type: Output
|
||||
}], expandedChange: [{
|
||||
type: Output
|
||||
}] } });
|
||||
//# sourceMappingURL=content-title-with-suggestions.component.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"content-title-with-suggestions.component.js","sourceRoot":"","sources":["../../../src/angular/content-title-with-suggestions.component.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEhG,OAAO,EAAE,mCAAmC,EAAE,MAAM,2CAA2C,CAAC;;AAwBhG,MAAM,OAAO,oCAAoC;IACtC,SAAS,CAAqB;IAC9B,IAAI,CAA4B;IAChC,QAAQ,CAA2C;IACnD,IAAI,GAAG,KAAK,CAAC;IACb,KAAK,CAAqD;IAC1D,MAAM,CAA2B;IACjC,aAAa,CAAqC;IAClD,SAAS,CAAiC;IAC1C,UAAU,CAAkC;IAElC,MAAM,GAAG,IAAI,YAAY,EAAQ,CAAC;IAClC,cAAc,GAAG,IAAI,YAAY,EAAW,CAAC;IAEhE,IAAI,kBAAkB;QACpB,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC,QAAQ,CAAC;QACvB,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,CAAC,EAAsC,CAAC;IACzD,CAAC;wGApBU,oCAAoC;4FAApC,oCAAoC,qWAjBrC;;;;;;;;;;;;;;;GAeT,4DAjBS,mCAAmC;;4FAmBlC,oCAAoC;kBAtBhD,SAAS;mBAAC;oBACT,QAAQ,EAAE,gCAAgC;oBAC1C,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,mCAAmC,CAAC;oBAC9C,eAAe,EAAE,uBAAuB,CAAC,MAAM;oBAC/C,QAAQ,EAAE;;;;;;;;;;;;;;;GAeT;iBACF;;sBAEE,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBAEL,MAAM;;sBACN,MAAM"}
|
||||
18
packages/content-suggestions/dist/angular/angular/index.d.ts
vendored
Normal file
18
packages/content-suggestions/dist/angular/angular/index.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import { ContentTextComponent } from "./content-text.component";
|
||||
import { ContentTextWithSuggestionsComponent } from "./content-text-with-suggestions.component";
|
||||
import { ContentTitleWithSuggestionsComponent } from "./content-title-with-suggestions.component";
|
||||
import { ContentSuggestionsRenderResultDirective } from "./render-result.directive";
|
||||
import * as i0 from "@angular/core";
|
||||
import * as i1 from "./content-text.component";
|
||||
import * as i2 from "./content-text-with-suggestions.component";
|
||||
import * as i3 from "./content-title-with-suggestions.component";
|
||||
import * as i4 from "./render-result.directive";
|
||||
export type { AngularContentEllipsisConfig, AngularContentSnapshot, AngularContentTextProps, AngularContentTextWithSuggestionsProps, AngularContentTitleWithSuggestionsProps, AngularContentToken, AngularCountEllipsisConfig, AngularEllipsisExpandHandler, AngularEllipsisSymbol, AngularExpandInfo, AngularLinkRenderer, AngularLinkToken, AngularMentionRenderer, AngularMentionToken, AngularRenderResult, AngularRowsEllipsisConfig, AngularTagRenderer, AngularTagToken, AngularTextToken, } from "./types";
|
||||
export { AngularContentSuggestionsAdapter, buildAngularTagHref, createAngularContentTokens, } from "./tokens";
|
||||
export { ContentTextComponent, ContentTextWithSuggestionsComponent, ContentTitleWithSuggestionsComponent, ContentSuggestionsRenderResultDirective, };
|
||||
export declare class ContentSuggestionsAngularModule {
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<ContentSuggestionsAngularModule, never>;
|
||||
static ɵmod: i0.ɵɵNgModuleDeclaration<ContentSuggestionsAngularModule, never, [typeof i1.ContentTextComponent, typeof i2.ContentTextWithSuggestionsComponent, typeof i3.ContentTitleWithSuggestionsComponent, typeof i4.ContentSuggestionsRenderResultDirective], [typeof i1.ContentTextComponent, typeof i2.ContentTextWithSuggestionsComponent, typeof i3.ContentTitleWithSuggestionsComponent, typeof i4.ContentSuggestionsRenderResultDirective]>;
|
||||
static ɵinj: i0.ɵɵInjectorDeclaration<ContentSuggestionsAngularModule>;
|
||||
}
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/angular/index.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/index.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/angular/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,mCAAmC,EAAE,MAAM,2CAA2C,CAAC;AAChG,OAAO,EAAE,oCAAoC,EAAE,MAAM,4CAA4C,CAAC;AAClG,OAAO,EAAE,uCAAuC,EAAE,MAAM,2BAA2B,CAAC;;;;;;AAEpF,YAAY,EACV,4BAA4B,EAC5B,sBAAsB,EACtB,uBAAuB,EACvB,sCAAsC,EACtC,uCAAuC,EACvC,mBAAmB,EACnB,0BAA0B,EAC1B,4BAA4B,EAC5B,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,yBAAyB,EACzB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,gCAAgC,EAChC,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,oBAAoB,EACpB,mCAAmC,EACnC,oCAAoC,EACpC,uCAAuC,GACxC,CAAC;AAEF,qBAca,+BAA+B;yCAA/B,+BAA+B;0CAA/B,+BAA+B;0CAA/B,+BAA+B;CAAG"}
|
||||
39
packages/content-suggestions/dist/angular/angular/index.js
vendored
Normal file
39
packages/content-suggestions/dist/angular/angular/index.js
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { ContentTextComponent } from "./content-text.component";
|
||||
import { ContentTextWithSuggestionsComponent } from "./content-text-with-suggestions.component";
|
||||
import { ContentTitleWithSuggestionsComponent } from "./content-title-with-suggestions.component";
|
||||
import { ContentSuggestionsRenderResultDirective } from "./render-result.directive";
|
||||
import * as i0 from "@angular/core";
|
||||
export { AngularContentSuggestionsAdapter, buildAngularTagHref, createAngularContentTokens, } from "./tokens";
|
||||
export { ContentTextComponent, ContentTextWithSuggestionsComponent, ContentTitleWithSuggestionsComponent, ContentSuggestionsRenderResultDirective, };
|
||||
export class ContentSuggestionsAngularModule {
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentSuggestionsAngularModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
||||
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.18", ngImport: i0, type: ContentSuggestionsAngularModule, imports: [ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective], exports: [ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective] });
|
||||
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentSuggestionsAngularModule, imports: [ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent] });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentSuggestionsAngularModule, decorators: [{
|
||||
type: NgModule,
|
||||
args: [{
|
||||
imports: [
|
||||
ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
],
|
||||
exports: [
|
||||
ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
],
|
||||
}]
|
||||
}] });
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/content-suggestions/dist/angular/angular/index.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/angular/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,mCAAmC,EAAE,MAAM,2CAA2C,CAAC;AAChG,OAAO,EAAE,oCAAoC,EAAE,MAAM,4CAA4C,CAAC;AAClG,OAAO,EAAE,uCAAuC,EAAE,MAAM,2BAA2B,CAAC;;AAwBpF,OAAO,EACL,gCAAgC,EAChC,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,oBAAoB,EACpB,mCAAmC,EACnC,oCAAoC,EACpC,uCAAuC,GACxC,CAAC;AAgBF,MAAM,OAAO,+BAA+B;wGAA/B,+BAA+B;yGAA/B,+BAA+B,YAZxC,oBAAoB;YACpB,mCAAmC;YACnC,oCAAoC;YACpC,uCAAuC,aAGvC,oBAAoB;YACpB,mCAAmC;YACnC,oCAAoC;YACpC,uCAAuC;yGAG9B,+BAA+B,YAZxC,oBAAoB;YACpB,mCAAmC;YACnC,oCAAoC;;4FAU3B,+BAA+B;kBAd3C,QAAQ;mBAAC;oBACR,OAAO,EAAE;wBACP,oBAAoB;wBACpB,mCAAmC;wBACnC,oCAAoC;wBACpC,uCAAuC;qBACxC;oBACD,OAAO,EAAE;wBACP,oBAAoB;wBACpB,mCAAmC;wBACnC,oCAAoC;wBACpC,uCAAuC;qBACxC;iBACF"}
|
||||
2
packages/content-suggestions/dist/angular/angular/public-api.d.ts
vendored
Normal file
2
packages/content-suggestions/dist/angular/angular/public-api.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./index";
|
||||
//# sourceMappingURL=public-api.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/angular/public-api.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/public-api.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../../../src/angular/public-api.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC"}
|
||||
2
packages/content-suggestions/dist/angular/angular/public-api.js
vendored
Normal file
2
packages/content-suggestions/dist/angular/angular/public-api.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./index";
|
||||
//# sourceMappingURL=public-api.js.map
|
||||
1
packages/content-suggestions/dist/angular/angular/public-api.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/public-api.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../../src/angular/public-api.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC"}
|
||||
14
packages/content-suggestions/dist/angular/angular/render-result.directive.d.ts
vendored
Normal file
14
packages/content-suggestions/dist/angular/angular/render-result.directive.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { AngularRenderResult } from "./types";
|
||||
import { ElementRef, OnChanges, Renderer2 } from "@angular/core";
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class ContentSuggestionsRenderResultDirective implements OnChanges {
|
||||
private readonly elementRef;
|
||||
private readonly renderer;
|
||||
contentSuggestionsRenderResult: AngularRenderResult;
|
||||
constructor(elementRef: ElementRef<HTMLElement>, renderer: Renderer2);
|
||||
ngOnChanges(): void;
|
||||
private clearHost;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<ContentSuggestionsRenderResultDirective, never>;
|
||||
static ɵdir: i0.ɵɵDirectiveDeclaration<ContentSuggestionsRenderResultDirective, "[contentSuggestionsRenderResult]", never, { "contentSuggestionsRenderResult": { "alias": "contentSuggestionsRenderResult"; "required": false; }; }, {}, never, never, true, never>;
|
||||
}
|
||||
//# sourceMappingURL=render-result.directive.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/angular/render-result.directive.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/render-result.directive.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"render-result.directive.d.ts","sourceRoot":"","sources":["../../../src/angular/render-result.directive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAa,UAAU,EAAS,SAAS,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;;AAEnF,qBAIa,uCAAwC,YAAW,SAAS;IAIrE,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJlB,8BAA8B,EAAE,mBAAmB,CAAC;gBAG1C,UAAU,EAAE,UAAU,CAAC,WAAW,CAAC,EACnC,QAAQ,EAAE,SAAS;IAKtC,WAAW,IAAI,IAAI;IAiBnB,OAAO,CAAC,SAAS;yCA3BN,uCAAuC;2CAAvC,uCAAuC;CAkCnD"}
|
||||
43
packages/content-suggestions/dist/angular/angular/render-result.directive.js
vendored
Normal file
43
packages/content-suggestions/dist/angular/angular/render-result.directive.js
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Directive, Input } from "@angular/core";
|
||||
import * as i0 from "@angular/core";
|
||||
export class ContentSuggestionsRenderResultDirective {
|
||||
elementRef;
|
||||
renderer;
|
||||
contentSuggestionsRenderResult;
|
||||
constructor(elementRef, renderer) {
|
||||
this.elementRef = elementRef;
|
||||
this.renderer = renderer;
|
||||
this.renderer.setStyle(this.elementRef.nativeElement, "display", "contents");
|
||||
}
|
||||
ngOnChanges() {
|
||||
this.clearHost();
|
||||
const value = this.contentSuggestionsRenderResult;
|
||||
if (value === null || value === undefined) {
|
||||
return;
|
||||
}
|
||||
if (value instanceof Node) {
|
||||
this.renderer.appendChild(this.elementRef.nativeElement, value);
|
||||
return;
|
||||
}
|
||||
const textNode = this.renderer.createText(String(value));
|
||||
this.renderer.appendChild(this.elementRef.nativeElement, textNode);
|
||||
}
|
||||
clearHost() {
|
||||
const host = this.elementRef.nativeElement;
|
||||
while (host.firstChild) {
|
||||
host.removeChild(host.firstChild);
|
||||
}
|
||||
}
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentSuggestionsRenderResultDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
|
||||
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.18", type: ContentSuggestionsRenderResultDirective, isStandalone: true, selector: "[contentSuggestionsRenderResult]", inputs: { contentSuggestionsRenderResult: "contentSuggestionsRenderResult" }, usesOnChanges: true, ngImport: i0 });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentSuggestionsRenderResultDirective, decorators: [{
|
||||
type: Directive,
|
||||
args: [{
|
||||
selector: "[contentSuggestionsRenderResult]",
|
||||
standalone: true,
|
||||
}]
|
||||
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { contentSuggestionsRenderResult: [{
|
||||
type: Input
|
||||
}] } });
|
||||
//# sourceMappingURL=render-result.directive.js.map
|
||||
1
packages/content-suggestions/dist/angular/angular/render-result.directive.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/render-result.directive.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"render-result.directive.js","sourceRoot":"","sources":["../../../src/angular/render-result.directive.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAc,KAAK,EAAwB,MAAM,eAAe,CAAC;;AAMnF,MAAM,OAAO,uCAAuC;IAI/B;IACA;IAJV,8BAA8B,CAAsB;IAE7D,YACmB,UAAmC,EACnC,QAAmB;QADnB,eAAU,GAAV,UAAU,CAAyB;QACnC,aAAQ,GAAR,QAAQ,CAAW;QAEpC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAC/E,CAAC;IAED,WAAW;QACT,IAAI,CAAC,SAAS,EAAE,CAAC;QAEjB,MAAM,KAAK,GAAG,IAAI,CAAC,8BAA8B,CAAC;QAClD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC1C,OAAO;QACT,CAAC;QAED,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;YAC1B,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;YAChE,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IACrE,CAAC;IAEO,SAAS;QACf,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;QAE3C,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC;YACvB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;wGAjCU,uCAAuC;4FAAvC,uCAAuC;;4FAAvC,uCAAuC;kBAJnD,SAAS;mBAAC;oBACT,QAAQ,EAAE,kCAAkC;oBAC5C,UAAU,EAAE,IAAI;iBACjB;;sBAEE,KAAK"}
|
||||
8
packages/content-suggestions/dist/angular/angular/tokens.d.ts
vendored
Normal file
8
packages/content-suggestions/dist/angular/angular/tokens.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { AngularContentSnapshot, AngularContentToken } from "./types";
|
||||
import type { TagEntity } from "../core";
|
||||
export declare const createAngularContentTokens: (inputText: string | null | undefined) => AngularContentToken[];
|
||||
export declare const buildAngularTagHref: (entity: TagEntity) => string;
|
||||
export declare class AngularContentSuggestionsAdapter {
|
||||
snapshot(inputText: string | null | undefined): AngularContentSnapshot;
|
||||
}
|
||||
//# sourceMappingURL=tokens.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/angular/tokens.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/tokens.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../src/angular/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,mBAAmB,EAKpB,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,SAAS,CAAC;AAgCxD,eAAO,MAAM,0BAA0B,GACrC,WAAW,MAAM,GAAG,IAAI,GAAG,SAAS,KACnC,mBAAmB,EAqBrB,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,QAAQ,SAAS,KAAG,MAEvD,CAAC;AAEF,qBAAa,gCAAgC;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,sBAAsB;CAWvE"}
|
||||
58
packages/content-suggestions/dist/angular/angular/tokens.js
vendored
Normal file
58
packages/content-suggestions/dist/angular/angular/tokens.js
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
import { findAllEntities } from "../core";
|
||||
const mapEntityToToken = (entity) => {
|
||||
if (entity.type === "mention") {
|
||||
return {
|
||||
kind: "mention",
|
||||
entity,
|
||||
};
|
||||
}
|
||||
if (entity.type === "tag") {
|
||||
return {
|
||||
kind: "tag",
|
||||
entity,
|
||||
};
|
||||
}
|
||||
return {
|
||||
kind: "link",
|
||||
entity,
|
||||
};
|
||||
};
|
||||
const createTextToken = (text, start, end) => ({
|
||||
kind: "text",
|
||||
text,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
export const createAngularContentTokens = (inputText) => {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
let cursor = 0;
|
||||
const tokens = [];
|
||||
for (const entity of entities) {
|
||||
if (entity.start > cursor) {
|
||||
tokens.push(createTextToken(text.slice(cursor, entity.start), cursor, entity.start));
|
||||
}
|
||||
tokens.push(mapEntityToToken(entity));
|
||||
cursor = entity.end;
|
||||
}
|
||||
if (cursor < text.length) {
|
||||
tokens.push(createTextToken(text.slice(cursor), cursor, text.length));
|
||||
}
|
||||
return tokens;
|
||||
};
|
||||
export const buildAngularTagHref = (entity) => {
|
||||
return `/search/?query=${encodeURIComponent(entity.tag.toLowerCase())}`;
|
||||
};
|
||||
export class AngularContentSuggestionsAdapter {
|
||||
snapshot(inputText) {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
const tokens = createAngularContentTokens(text);
|
||||
return {
|
||||
text,
|
||||
entities,
|
||||
tokens,
|
||||
};
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=tokens.js.map
|
||||
1
packages/content-suggestions/dist/angular/angular/tokens.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/tokens.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../../src/angular/tokens.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,gBAAgB,GAAG,CAAC,MAAqB,EAA4D,EAAE;IAC3G,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO;YACL,IAAI,EAAE,SAAS;YACf,MAAM;SACP,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO;YACL,IAAI,EAAE,KAAK;YACX,MAAM;SACP,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,MAAM;KACP,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,IAAY,EAAE,KAAa,EAAE,GAAW,EAAoB,EAAE,CAAC,CAAC;IACvF,IAAI,EAAE,MAAM;IACZ,IAAI;IACJ,KAAK;IACL,GAAG;CACJ,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,SAAoC,EACb,EAAE;IACzB,MAAM,IAAI,GAAG,SAAS,IAAI,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAEvC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,MAAM,GAA0B,EAAE,CAAC;IAEzC,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC9B,IAAI,MAAM,CAAC,KAAK,GAAG,MAAM,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACvF,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;QACtC,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC;IACtB,CAAC;IAED,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAiB,EAAU,EAAE;IAC/D,OAAO,kBAAkB,kBAAkB,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;AAC1E,CAAC,CAAC;AAEF,MAAM,OAAO,gCAAgC;IAC3C,QAAQ,CAAC,SAAoC;QAC3C,MAAM,IAAI,GAAG,SAAS,IAAI,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,0BAA0B,CAAC,IAAI,CAAC,CAAC;QAEhD,OAAO;YACL,IAAI;YACJ,QAAQ;YACR,MAAM;SACP,CAAC;IACJ,CAAC;CACF"}
|
||||
70
packages/content-suggestions/dist/angular/angular/types.d.ts
vendored
Normal file
70
packages/content-suggestions/dist/angular/angular/types.d.ts
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
import type { ContentEntity, LinkEntity, MentionEntity, TagEntity } from "../core";
|
||||
import type { TypographyWeight } from "@hublib-web/tach-typography/core";
|
||||
export interface AngularTextToken {
|
||||
kind: "text";
|
||||
text: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
export interface AngularMentionToken {
|
||||
kind: "mention";
|
||||
entity: MentionEntity;
|
||||
}
|
||||
export interface AngularTagToken {
|
||||
kind: "tag";
|
||||
entity: TagEntity;
|
||||
}
|
||||
export interface AngularLinkToken {
|
||||
kind: "link";
|
||||
entity: LinkEntity;
|
||||
}
|
||||
export type AngularContentToken = AngularTextToken | AngularMentionToken | AngularTagToken | AngularLinkToken;
|
||||
export interface AngularContentSnapshot {
|
||||
text: string;
|
||||
entities: ContentEntity[];
|
||||
tokens: AngularContentToken[];
|
||||
}
|
||||
export interface AngularExpandInfo {
|
||||
expanded: boolean;
|
||||
}
|
||||
export type AngularEllipsisSymbol = string | ((expanded: boolean) => string);
|
||||
export type AngularEllipsisExpandHandler = ((expanded: boolean) => void) | ((event: MouseEvent, info: AngularExpandInfo) => void);
|
||||
export type AngularCountEllipsisConfig = {
|
||||
count: number;
|
||||
rows?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: AngularEllipsisExpandHandler;
|
||||
};
|
||||
export type AngularRowsEllipsisConfig = {
|
||||
rows: number;
|
||||
count?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: AngularEllipsisExpandHandler;
|
||||
};
|
||||
export type AngularContentEllipsisConfig = AngularCountEllipsisConfig | AngularRowsEllipsisConfig | false;
|
||||
export type AngularRenderResult = Node | string | number | null | undefined;
|
||||
export type AngularMentionRenderer = (entity: MentionEntity, index: number) => AngularRenderResult;
|
||||
export type AngularTagRenderer = (entity: TagEntity, index: number) => AngularRenderResult;
|
||||
export type AngularLinkRenderer = (entity: LinkEntity, index: number) => AngularRenderResult;
|
||||
export interface AngularContentTextProps {
|
||||
className?: string;
|
||||
weight?: TypographyWeight;
|
||||
text?: string | null;
|
||||
ellipsis?: AngularContentEllipsisConfig;
|
||||
blur?: boolean;
|
||||
style?: Record<string, string | number> | null;
|
||||
onView?: () => void;
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
renderLink?: AngularLinkRenderer;
|
||||
}
|
||||
export type AngularContentTextWithSuggestionsProps = Omit<AngularContentTextProps, "renderMention" | "renderTag"> & {
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
};
|
||||
export type AngularContentTitleWithSuggestionsProps = Omit<AngularContentTextWithSuggestionsProps, "weight">;
|
||||
//# sourceMappingURL=types.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/angular/types.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/types.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/angular/types.ts"],"names":[],"mappings":"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"}
|
||||
2
packages/content-suggestions/dist/angular/angular/types.js
vendored
Normal file
2
packages/content-suggestions/dist/angular/angular/types.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=types.js.map
|
||||
1
packages/content-suggestions/dist/angular/angular/types.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/types.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/angular/types.ts"],"names":[],"mappings":""}
|
||||
3
packages/content-suggestions/dist/angular/core/index.d.ts
vendored
Normal file
3
packages/content-suggestions/dist/angular/core/index.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export type { BaseEntity, ContentEntity, LinkEntity, MentionEntity, ParsedMention, ProcessedContent, TagEntity, } from "./types";
|
||||
export { findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent, } from "./parser";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/core/index.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/core/index.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,UAAU,EACV,aAAa,EACb,UAAU,EACV,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,eAAe,EACf,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,cAAc,GACf,MAAM,UAAU,CAAC"}
|
||||
2
packages/content-suggestions/dist/angular/core/index.js
vendored
Normal file
2
packages/content-suggestions/dist/angular/core/index.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export { findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent, } from "./parser";
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/content-suggestions/dist/angular/core/index.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/core/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,eAAe,EACf,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,cAAc,GACf,MAAM,UAAU,CAAC"}
|
||||
9
packages/content-suggestions/dist/angular/core/parser.d.ts
vendored
Normal file
9
packages/content-suggestions/dist/angular/core/parser.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { ContentEntity, LinkEntity, MentionEntity, ParsedMention, ProcessedContent, TagEntity } from "./types";
|
||||
export declare const mentionLinkRegexp: RegExp;
|
||||
export declare const parseMention: (mention: string) => ParsedMention | null;
|
||||
export declare const findMentions: (text: string) => MentionEntity[];
|
||||
export declare const findTags: (content: string) => TagEntity[];
|
||||
export declare const findLinks: (content: string) => LinkEntity[];
|
||||
export declare const findAllEntities: (content: string) => ContentEntity[];
|
||||
export declare const processContent: (content: string) => ProcessedContent;
|
||||
//# sourceMappingURL=parser.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/core/parser.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/core/parser.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/core/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,UAAU,EACV,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,SAAS,EACV,MAAM,SAAS,CAAC;AAEjB,eAAO,MAAM,iBAAiB,QACgE,CAAC;AAE/F,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,KAAG,aAAa,GAAG,IAkB9D,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,aAAa,EAkBxD,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,SAAS,MAAM,KAAG,SAAS,EAiBnD,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,SAAS,MAAM,KAAG,UAAU,EAqBrD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,KAAG,aAAa,EAM9D,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,gBAYhD,CAAC"}
|
||||
85
packages/content-suggestions/dist/angular/core/parser.js
vendored
Normal file
85
packages/content-suggestions/dist/angular/core/parser.js
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
export const mentionLinkRegexp = /@\[[^\]]+]\([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\)/g;
|
||||
export const parseMention = (mention) => {
|
||||
const regex = /@\[([^\]]+)\]\(([\w-]{36})\)/;
|
||||
const match = mention.match(regex);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
const mentionText = match[1];
|
||||
const mentionId = match[2];
|
||||
if (!mentionText || !mentionId) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
mention: `@${mentionText}`,
|
||||
id: mentionId,
|
||||
};
|
||||
};
|
||||
export const findMentions = (text) => {
|
||||
let match;
|
||||
const matches = [];
|
||||
while ((match = mentionLinkRegexp.exec(text)) !== null) {
|
||||
const parsed = parseMention(match[0]);
|
||||
const baseMention = {
|
||||
start: match.index,
|
||||
end: mentionLinkRegexp.lastIndex,
|
||||
text: match[0],
|
||||
type: "mention",
|
||||
displayText: parsed?.mention ?? match[0],
|
||||
};
|
||||
matches.push(parsed?.id ? { ...baseMention, userId: parsed.id } : baseMention);
|
||||
}
|
||||
return matches;
|
||||
};
|
||||
export const findTags = (content) => {
|
||||
const regex = /#[^\s]{1,201}/g;
|
||||
const results = [];
|
||||
let match;
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
const value = match[0];
|
||||
results.push({
|
||||
start: match.index,
|
||||
end: match.index + value.length,
|
||||
text: value,
|
||||
type: "tag",
|
||||
tag: value.replace("#", ""),
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
export const findLinks = (content) => {
|
||||
const regex = /\b((https?:\/\/)?(?:[\w-]+\.)+[a-z]{2,}(\/[\w\-._~:/?#[\]@!$&'()*+,;=]*)?)/gi;
|
||||
const results = [];
|
||||
let match;
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
const rawUrl = match[0];
|
||||
const hasProtocol = /^https?:\/\//i.test(rawUrl);
|
||||
const fullUrl = hasProtocol ? rawUrl : `https://${rawUrl}`;
|
||||
results.push({
|
||||
start: match.index,
|
||||
end: match.index + rawUrl.length,
|
||||
text: rawUrl,
|
||||
url: fullUrl,
|
||||
type: "link",
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
export const findAllEntities = (content) => {
|
||||
const mentions = findMentions(content);
|
||||
const tags = findTags(content);
|
||||
const links = findLinks(content);
|
||||
return [...mentions, ...tags, ...links].sort((a, b) => a.start - b.start);
|
||||
};
|
||||
export const processContent = (content) => {
|
||||
const processedText = content.replace(mentionLinkRegexp, match => {
|
||||
const parsed = parseMention(match);
|
||||
return parsed ? parsed.mention : match;
|
||||
});
|
||||
const tags = findTags(content).map(tag => tag.tag);
|
||||
return {
|
||||
processedText,
|
||||
tags,
|
||||
};
|
||||
};
|
||||
//# sourceMappingURL=parser.js.map
|
||||
1
packages/content-suggestions/dist/angular/core/parser.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/core/parser.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../src/core/parser.ts"],"names":[],"mappings":"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"}
|
||||
28
packages/content-suggestions/dist/angular/core/types.d.ts
vendored
Normal file
28
packages/content-suggestions/dist/angular/core/types.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
export interface BaseEntity {
|
||||
start: number;
|
||||
end: number;
|
||||
text: string;
|
||||
}
|
||||
export interface MentionEntity extends BaseEntity {
|
||||
type: "mention";
|
||||
displayText: string;
|
||||
userId?: string;
|
||||
}
|
||||
export interface TagEntity extends BaseEntity {
|
||||
type: "tag";
|
||||
tag: string;
|
||||
}
|
||||
export interface LinkEntity extends BaseEntity {
|
||||
type: "link";
|
||||
url: string;
|
||||
}
|
||||
export type ContentEntity = MentionEntity | TagEntity | LinkEntity;
|
||||
export interface ParsedMention {
|
||||
mention: string;
|
||||
id: string;
|
||||
}
|
||||
export interface ProcessedContent {
|
||||
processedText: string;
|
||||
tags: string[];
|
||||
}
|
||||
//# sourceMappingURL=types.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/core/types.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/core/types.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAU,SAAQ,UAAU;IAC3C,IAAI,EAAE,KAAK,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAW,SAAQ,UAAU;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,SAAS,GAAG,UAAU,CAAC;AAEnE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB"}
|
||||
2
packages/content-suggestions/dist/angular/core/types.js
vendored
Normal file
2
packages/content-suggestions/dist/angular/core/types.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=types.js.map
|
||||
1
packages/content-suggestions/dist/angular/core/types.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/core/types.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/types.ts"],"names":[],"mappings":""}
|
||||
495
packages/content-suggestions/dist/angular/index.cjs
vendored
495
packages/content-suggestions/dist/angular/index.cjs
vendored
@@ -1,495 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// src/core/parser.ts
|
||||
var mentionLinkRegexp = /@\[[^\]]+]\([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\)/g;
|
||||
var parseMention = (mention) => {
|
||||
const regex = /@\[([^\]]+)\]\(([\w-]{36})\)/;
|
||||
const match = mention.match(regex);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
const mentionText = match[1];
|
||||
const mentionId = match[2];
|
||||
if (!mentionText || !mentionId) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
mention: `@${mentionText}`,
|
||||
id: mentionId
|
||||
};
|
||||
};
|
||||
var findMentions = (text) => {
|
||||
let match;
|
||||
const matches = [];
|
||||
while ((match = mentionLinkRegexp.exec(text)) !== null) {
|
||||
const parsed = parseMention(match[0]);
|
||||
const baseMention = {
|
||||
start: match.index,
|
||||
end: mentionLinkRegexp.lastIndex,
|
||||
text: match[0],
|
||||
type: "mention",
|
||||
displayText: parsed?.mention ?? match[0]
|
||||
};
|
||||
matches.push(parsed?.id ? { ...baseMention, userId: parsed.id } : baseMention);
|
||||
}
|
||||
return matches;
|
||||
};
|
||||
var findTags = (content) => {
|
||||
const regex = /#[^\s]{1,201}/g;
|
||||
const results = [];
|
||||
let match;
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
const value = match[0];
|
||||
results.push({
|
||||
start: match.index,
|
||||
end: match.index + value.length,
|
||||
text: value,
|
||||
type: "tag",
|
||||
tag: value.replace("#", "")
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
var findLinks = (content) => {
|
||||
const regex = /\b((https?:\/\/)?(?:[\w-]+\.)+[a-z]{2,}(\/[\w\-._~:/?#[\]@!$&'()*+,;=]*)?)/gi;
|
||||
const results = [];
|
||||
let match;
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
const rawUrl = match[0];
|
||||
const hasProtocol = /^https?:\/\//i.test(rawUrl);
|
||||
const fullUrl = hasProtocol ? rawUrl : `https://${rawUrl}`;
|
||||
results.push({
|
||||
start: match.index,
|
||||
end: match.index + rawUrl.length,
|
||||
text: rawUrl,
|
||||
url: fullUrl,
|
||||
type: "link"
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
var findAllEntities = (content) => {
|
||||
const mentions = findMentions(content);
|
||||
const tags = findTags(content);
|
||||
const links = findLinks(content);
|
||||
return [...mentions, ...tags, ...links].sort((a, b) => a.start - b.start);
|
||||
};
|
||||
|
||||
// src/angular/index.ts
|
||||
var LINK_COLOR = "#1677ff";
|
||||
var READ_MORE_TEXT = "\u0427\u0438\u0442\u0430\u0442\u044C \u043F\u043E\u043B\u043D\u043E\u0441\u0442\u044C\u044E";
|
||||
var toKebabCase = (value) => value.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
|
||||
var applyStyleObject = (element, styles) => {
|
||||
if (!styles) {
|
||||
return;
|
||||
}
|
||||
for (const [key, rawValue] of Object.entries(styles)) {
|
||||
if (rawValue === null || rawValue === void 0) {
|
||||
continue;
|
||||
}
|
||||
const styleValue = typeof rawValue === "number" ? `${rawValue}px` : String(rawValue);
|
||||
if (key.startsWith("--")) {
|
||||
element.style.setProperty(key, styleValue);
|
||||
continue;
|
||||
}
|
||||
const cssKey = key.includes("-") ? key : toKebabCase(key);
|
||||
element.style.setProperty(cssKey, styleValue);
|
||||
}
|
||||
};
|
||||
var toNode = (value) => {
|
||||
if (value === null || value === void 0) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Node) {
|
||||
return value;
|
||||
}
|
||||
return document.createTextNode(String(value));
|
||||
};
|
||||
var resolveEllipsisSymbol = (symbol, expanded) => {
|
||||
if (typeof symbol === "function") {
|
||||
return symbol(expanded);
|
||||
}
|
||||
return symbol ?? READ_MORE_TEXT;
|
||||
};
|
||||
var buildAngularTagHref = (entity) => {
|
||||
return `/search/?query=${encodeURIComponent(entity.tag.toLowerCase())}`;
|
||||
};
|
||||
var createAngularContentTokens = (inputText) => {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
let cursor = 0;
|
||||
const tokens = [];
|
||||
for (const entity of entities) {
|
||||
if (entity.start > cursor) {
|
||||
tokens.push({
|
||||
kind: "text",
|
||||
text: text.slice(cursor, entity.start),
|
||||
start: cursor,
|
||||
end: entity.start
|
||||
});
|
||||
}
|
||||
if (entity.type === "mention") {
|
||||
tokens.push({
|
||||
kind: "mention",
|
||||
entity
|
||||
});
|
||||
} else if (entity.type === "tag") {
|
||||
tokens.push({
|
||||
kind: "tag",
|
||||
entity
|
||||
});
|
||||
} else {
|
||||
tokens.push({
|
||||
kind: "link",
|
||||
entity
|
||||
});
|
||||
}
|
||||
cursor = entity.end;
|
||||
}
|
||||
if (cursor < text.length) {
|
||||
tokens.push({
|
||||
kind: "text",
|
||||
text: text.slice(cursor),
|
||||
start: cursor,
|
||||
end: text.length
|
||||
});
|
||||
}
|
||||
return tokens;
|
||||
};
|
||||
var AngularContentSuggestionsAdapter = class {
|
||||
snapshot(inputText) {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
const tokens = createAngularContentTokens(text);
|
||||
return {
|
||||
text,
|
||||
entities,
|
||||
tokens
|
||||
};
|
||||
}
|
||||
};
|
||||
var AngularContentTextRenderer = class {
|
||||
host = null;
|
||||
props = {};
|
||||
expanded = false;
|
||||
viewed = false;
|
||||
hostInsideView = false;
|
||||
observer = null;
|
||||
attach(host, props = {}) {
|
||||
this.destroy();
|
||||
this.host = host;
|
||||
this.props = { ...props };
|
||||
this.expanded = false;
|
||||
this.viewed = false;
|
||||
this.hostInsideView = false;
|
||||
this.initObserver();
|
||||
this.render();
|
||||
return this.getState();
|
||||
}
|
||||
update(nextProps) {
|
||||
this.props = {
|
||||
...this.props,
|
||||
...nextProps
|
||||
};
|
||||
this.render();
|
||||
return this.getState();
|
||||
}
|
||||
destroy() {
|
||||
if (this.observer && this.host) {
|
||||
this.observer.unobserve(this.host);
|
||||
this.observer.disconnect();
|
||||
}
|
||||
this.observer = null;
|
||||
if (this.host) {
|
||||
this.host.innerHTML = "";
|
||||
}
|
||||
this.host = null;
|
||||
this.props = {};
|
||||
this.expanded = false;
|
||||
this.viewed = false;
|
||||
this.hostInsideView = false;
|
||||
}
|
||||
getState() {
|
||||
return {
|
||||
props: { ...this.props },
|
||||
snapshot: this.createSnapshot(),
|
||||
expanded: this.getMergedExpanded(this.resolveEllipsisConfig(), this.getText())
|
||||
};
|
||||
}
|
||||
getText() {
|
||||
return this.props.text ?? "";
|
||||
}
|
||||
createSnapshot() {
|
||||
const text = this.getText();
|
||||
const entities = findAllEntities(text);
|
||||
const tokens = createAngularContentTokens(text);
|
||||
return {
|
||||
text,
|
||||
entities,
|
||||
tokens
|
||||
};
|
||||
}
|
||||
resolveEllipsisConfig() {
|
||||
const ellipsis = this.props.ellipsis;
|
||||
if (!ellipsis || typeof ellipsis !== "object") {
|
||||
return null;
|
||||
}
|
||||
return ellipsis;
|
||||
}
|
||||
getMergedExpanded(ellipsisConfig, text) {
|
||||
const controlledExpanded = ellipsisConfig && typeof ellipsisConfig.expanded === "boolean" ? ellipsisConfig.expanded : void 0;
|
||||
if (controlledExpanded !== void 0) {
|
||||
return controlledExpanded;
|
||||
}
|
||||
if (ellipsisConfig && "count" in ellipsisConfig) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
if (count <= 0 || text.length <= count) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return this.expanded;
|
||||
}
|
||||
createDefaultMentionNode(entity) {
|
||||
const span = document.createElement("span");
|
||||
span.style.color = LINK_COLOR;
|
||||
span.style.fontWeight = this.props.weight === "bold" ? "700" : "400";
|
||||
span.textContent = entity.displayText;
|
||||
return span;
|
||||
}
|
||||
createDefaultTagNode(entity) {
|
||||
const span = document.createElement("span");
|
||||
span.style.color = LINK_COLOR;
|
||||
span.style.fontWeight = this.props.weight === "bold" ? "700" : "400";
|
||||
span.textContent = entity.text;
|
||||
return span;
|
||||
}
|
||||
createDefaultLinkNode(entity) {
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = entity.url;
|
||||
anchor.target = "_blank";
|
||||
anchor.referrerPolicy = "no-referrer";
|
||||
anchor.style.color = LINK_COLOR;
|
||||
anchor.style.fontWeight = this.props.weight === "bold" ? "700" : "400";
|
||||
anchor.textContent = entity.text;
|
||||
return anchor;
|
||||
}
|
||||
renderEntity(entity, index) {
|
||||
if (entity.type === "mention") {
|
||||
const customNode2 = this.props.renderMention?.(entity, index);
|
||||
return toNode(customNode2) ?? this.createDefaultMentionNode(entity);
|
||||
}
|
||||
if (entity.type === "tag") {
|
||||
const customNode2 = this.props.renderTag?.(entity, index);
|
||||
return toNode(customNode2) ?? this.createDefaultTagNode(entity);
|
||||
}
|
||||
const customNode = this.props.renderLink?.(entity, index);
|
||||
return toNode(customNode) ?? this.createDefaultLinkNode(entity);
|
||||
}
|
||||
buildTextNodes(text, entities, upto = null) {
|
||||
let lastIndex = 0;
|
||||
const nodes = [];
|
||||
for (const [index, entity] of entities.entries()) {
|
||||
if (upto !== null && entity.start >= upto) {
|
||||
break;
|
||||
}
|
||||
const textEnd = upto !== null ? Math.min(entity.start, upto) : entity.start;
|
||||
if (entity.start > lastIndex && lastIndex < textEnd) {
|
||||
nodes.push(document.createTextNode(text.slice(lastIndex, textEnd)));
|
||||
}
|
||||
if (upto === null || entity.end <= upto) {
|
||||
const entityNode = this.renderEntity(entity, index);
|
||||
if (entityNode) {
|
||||
nodes.push(entityNode);
|
||||
}
|
||||
}
|
||||
lastIndex = entity.end;
|
||||
}
|
||||
if (upto === null) {
|
||||
if (lastIndex < text.length) {
|
||||
nodes.push(document.createTextNode(text.slice(lastIndex)));
|
||||
}
|
||||
} else if (lastIndex < upto) {
|
||||
nodes.push(document.createTextNode(text.slice(lastIndex, upto)));
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
applyBaseParagraphStyle(element) {
|
||||
element.style.whiteSpace = "pre-wrap";
|
||||
element.style.fontWeight = this.props.weight === "bold" ? "700" : "400";
|
||||
element.style.setProperty("-webkit-touch-callout", "default");
|
||||
element.style.setProperty("-webkit-user-select", "text");
|
||||
element.style.setProperty("-khtml-user-select", "text");
|
||||
element.style.setProperty("-moz-user-select", "text");
|
||||
element.style.setProperty("-ms-user-select", "text");
|
||||
element.style.setProperty("user-select", "text");
|
||||
if (this.props.blur) {
|
||||
element.style.filter = "blur(3px)";
|
||||
element.style.setProperty("-webkit-user-select", "none");
|
||||
element.style.setProperty("-khtml-user-select", "none");
|
||||
element.style.setProperty("-moz-user-select", "none");
|
||||
element.style.setProperty("-ms-user-select", "none");
|
||||
element.style.setProperty("user-select", "none");
|
||||
element.style.pointerEvents = "none";
|
||||
}
|
||||
applyStyleObject(element, this.props.style);
|
||||
}
|
||||
buildReadMoreButton(symbol) {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.textContent = resolveEllipsisSymbol(symbol, this.expanded);
|
||||
button.style.border = "0";
|
||||
button.style.background = "none";
|
||||
button.style.padding = "0";
|
||||
button.style.marginLeft = "6px";
|
||||
button.style.cursor = "pointer";
|
||||
button.style.color = LINK_COLOR;
|
||||
button.style.fontWeight = "700";
|
||||
button.onclick = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.handleExpand();
|
||||
};
|
||||
return button;
|
||||
}
|
||||
handleExpand() {
|
||||
const ellipsisConfig = this.resolveEllipsisConfig();
|
||||
if (!ellipsisConfig) {
|
||||
return;
|
||||
}
|
||||
this.expanded = true;
|
||||
ellipsisConfig.onExpand?.(true);
|
||||
this.render();
|
||||
}
|
||||
computeEntitySafeCutoff(count, entities) {
|
||||
let cutoff = count;
|
||||
let extended = true;
|
||||
while (extended) {
|
||||
extended = false;
|
||||
for (const entity of entities) {
|
||||
if (entity.start < cutoff && entity.end > cutoff) {
|
||||
cutoff = entity.end;
|
||||
extended = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cutoff;
|
||||
}
|
||||
hasVerticalOverflow(element) {
|
||||
return element.scrollHeight - element.clientHeight > 1;
|
||||
}
|
||||
render() {
|
||||
if (!this.host) {
|
||||
return;
|
||||
}
|
||||
const text = this.getText();
|
||||
const entities = findAllEntities(text);
|
||||
const ellipsisConfig = this.resolveEllipsisConfig();
|
||||
const mergedExpanded = this.getMergedExpanded(ellipsisConfig, text);
|
||||
const wrapper = document.createElement("div");
|
||||
const paragraph = document.createElement("div");
|
||||
if (this.props.className) {
|
||||
paragraph.className = this.props.className;
|
||||
}
|
||||
this.applyBaseParagraphStyle(paragraph);
|
||||
if (ellipsisConfig && "count" in ellipsisConfig) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
const shouldTruncate = !mergedExpanded && count > 0 && text.length > count;
|
||||
if (shouldTruncate) {
|
||||
const cutoff = this.computeEntitySafeCutoff(count, entities);
|
||||
const nodes = this.buildTextNodes(text, entities, cutoff);
|
||||
paragraph.append(...nodes);
|
||||
paragraph.append(document.createTextNode("\u2026"));
|
||||
if (ellipsisConfig.expandable) {
|
||||
paragraph.append(this.buildReadMoreButton(ellipsisConfig.symbol));
|
||||
}
|
||||
} else {
|
||||
paragraph.append(...this.buildTextNodes(text, entities));
|
||||
}
|
||||
} else {
|
||||
paragraph.append(...this.buildTextNodes(text, entities));
|
||||
if (ellipsisConfig && "rows" in ellipsisConfig && !mergedExpanded) {
|
||||
paragraph.style.display = "-webkit-box";
|
||||
paragraph.style.setProperty("-webkit-box-orient", "vertical");
|
||||
paragraph.style.setProperty("-webkit-line-clamp", String(ellipsisConfig.rows));
|
||||
paragraph.style.overflow = "hidden";
|
||||
wrapper.append(paragraph);
|
||||
this.host.replaceChildren(wrapper);
|
||||
if (ellipsisConfig.expandable && this.hasVerticalOverflow(paragraph)) {
|
||||
wrapper.append(this.buildReadMoreButton(ellipsisConfig.symbol));
|
||||
}
|
||||
this.emitOnViewIfNeeded(mergedExpanded);
|
||||
return;
|
||||
}
|
||||
}
|
||||
wrapper.append(paragraph);
|
||||
this.host.replaceChildren(wrapper);
|
||||
this.emitOnViewIfNeeded(mergedExpanded);
|
||||
}
|
||||
initObserver() {
|
||||
if (!this.host) {
|
||||
return;
|
||||
}
|
||||
if (typeof IntersectionObserver === "undefined") {
|
||||
this.hostInsideView = true;
|
||||
return;
|
||||
}
|
||||
this.observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
const entry = entries[0];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
if (entry.isIntersecting && !this.hostInsideView) {
|
||||
this.hostInsideView = true;
|
||||
const ellipsisConfig = this.resolveEllipsisConfig();
|
||||
const mergedExpanded = this.getMergedExpanded(ellipsisConfig, this.getText());
|
||||
this.emitOnViewIfNeeded(mergedExpanded);
|
||||
}
|
||||
},
|
||||
{ threshold: 0.5 }
|
||||
);
|
||||
this.observer.observe(this.host);
|
||||
}
|
||||
emitOnViewIfNeeded(mergedExpanded) {
|
||||
if (!mergedExpanded || this.viewed || !this.hostInsideView) {
|
||||
return;
|
||||
}
|
||||
this.viewed = true;
|
||||
this.props.onView?.();
|
||||
}
|
||||
};
|
||||
var AngularContentTextWithSuggestionsRenderer = class extends AngularContentTextRenderer {
|
||||
};
|
||||
var AngularContentTitleWithSuggestionsRenderer = class {
|
||||
renderer = new AngularContentTextWithSuggestionsRenderer();
|
||||
attach(host, props = {}) {
|
||||
return this.renderer.attach(host, this.normalizeProps(props));
|
||||
}
|
||||
update(props) {
|
||||
return this.renderer.update(this.normalizeProps(props));
|
||||
}
|
||||
destroy() {
|
||||
this.renderer.destroy();
|
||||
}
|
||||
getState() {
|
||||
return this.renderer.getState();
|
||||
}
|
||||
normalizeProps(props) {
|
||||
return {
|
||||
...props,
|
||||
weight: "bold",
|
||||
blur: props.blur ?? false,
|
||||
ellipsis: props.ellipsis === void 0 ? { rows: 2 } : props.ellipsis
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
exports.AngularContentSuggestionsAdapter = AngularContentSuggestionsAdapter;
|
||||
exports.AngularContentTextRenderer = AngularContentTextRenderer;
|
||||
exports.AngularContentTextWithSuggestionsRenderer = AngularContentTextWithSuggestionsRenderer;
|
||||
exports.AngularContentTitleWithSuggestionsRenderer = AngularContentTitleWithSuggestionsRenderer;
|
||||
exports.buildAngularTagHref = buildAngularTagHref;
|
||||
exports.createAngularContentTokens = createAngularContentTokens;
|
||||
exports.toKebabCase = toKebabCase;
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,118 +0,0 @@
|
||||
import { C as ContentEntity, M as MentionEntity, T as TagEntity, L as LinkEntity } from '../types-BRt4hd7A.cjs';
|
||||
|
||||
interface AngularTextToken {
|
||||
kind: "text";
|
||||
text: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
interface AngularMentionToken {
|
||||
kind: "mention";
|
||||
entity: MentionEntity;
|
||||
}
|
||||
interface AngularTagToken {
|
||||
kind: "tag";
|
||||
entity: TagEntity;
|
||||
}
|
||||
interface AngularLinkToken {
|
||||
kind: "link";
|
||||
entity: LinkEntity;
|
||||
}
|
||||
type AngularContentToken = AngularTextToken | AngularMentionToken | AngularTagToken | AngularLinkToken;
|
||||
interface AngularContentSnapshot {
|
||||
text: string;
|
||||
entities: ContentEntity[];
|
||||
tokens: AngularContentToken[];
|
||||
}
|
||||
type AngularEllipsisSymbol = string | ((expanded: boolean) => string);
|
||||
type AngularCountEllipsisConfig = {
|
||||
count: number;
|
||||
rows?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: (expanded: boolean) => void;
|
||||
};
|
||||
type AngularRowsEllipsisConfig = {
|
||||
rows: number;
|
||||
count?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: (expanded: boolean) => void;
|
||||
};
|
||||
type AngularContentEllipsisConfig = AngularCountEllipsisConfig | AngularRowsEllipsisConfig | false;
|
||||
type AngularRenderResult = Node | string | number | null | undefined;
|
||||
type AngularMentionRenderer = (entity: MentionEntity, index: number) => AngularRenderResult;
|
||||
type AngularTagRenderer = (entity: TagEntity, index: number) => AngularRenderResult;
|
||||
type AngularLinkRenderer = (entity: LinkEntity, index: number) => AngularRenderResult;
|
||||
interface AngularContentTextProps {
|
||||
className?: string;
|
||||
weight?: "normal" | "bold";
|
||||
text?: string | null;
|
||||
ellipsis?: AngularContentEllipsisConfig;
|
||||
blur?: boolean;
|
||||
style?: Record<string, string | number> | null;
|
||||
onView?: () => void;
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
renderLink?: AngularLinkRenderer;
|
||||
}
|
||||
type AngularContentTextWithSuggestionsProps = Omit<AngularContentTextProps, "renderMention" | "renderTag"> & {
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
};
|
||||
type AngularContentTitleWithSuggestionsProps = Omit<AngularContentTextWithSuggestionsProps, "weight">;
|
||||
interface AngularContentTextRendererState {
|
||||
props: AngularContentTextProps;
|
||||
snapshot: AngularContentSnapshot;
|
||||
expanded: boolean;
|
||||
}
|
||||
declare const toKebabCase: (value: string) => string;
|
||||
declare const buildAngularTagHref: (entity: TagEntity) => string;
|
||||
|
||||
declare const createAngularContentTokens: (inputText: string | null | undefined) => AngularContentToken[];
|
||||
declare class AngularContentSuggestionsAdapter {
|
||||
snapshot(inputText: string | null | undefined): AngularContentSnapshot;
|
||||
}
|
||||
declare class AngularContentTextRenderer {
|
||||
private host;
|
||||
private props;
|
||||
private expanded;
|
||||
private viewed;
|
||||
private hostInsideView;
|
||||
private observer;
|
||||
attach(host: HTMLElement, props?: AngularContentTextProps): AngularContentTextRendererState;
|
||||
update(nextProps: AngularContentTextProps): AngularContentTextRendererState;
|
||||
destroy(): void;
|
||||
getState(): AngularContentTextRendererState;
|
||||
private getText;
|
||||
private createSnapshot;
|
||||
private resolveEllipsisConfig;
|
||||
private getMergedExpanded;
|
||||
private createDefaultMentionNode;
|
||||
private createDefaultTagNode;
|
||||
private createDefaultLinkNode;
|
||||
private renderEntity;
|
||||
private buildTextNodes;
|
||||
private applyBaseParagraphStyle;
|
||||
private buildReadMoreButton;
|
||||
private handleExpand;
|
||||
private computeEntitySafeCutoff;
|
||||
private hasVerticalOverflow;
|
||||
private render;
|
||||
private initObserver;
|
||||
private emitOnViewIfNeeded;
|
||||
}
|
||||
declare class AngularContentTextWithSuggestionsRenderer extends AngularContentTextRenderer {
|
||||
}
|
||||
declare class AngularContentTitleWithSuggestionsRenderer {
|
||||
private readonly renderer;
|
||||
attach(host: HTMLElement, props?: AngularContentTitleWithSuggestionsProps): AngularContentTextRendererState;
|
||||
update(props: AngularContentTitleWithSuggestionsProps): AngularContentTextRendererState;
|
||||
destroy(): void;
|
||||
getState(): AngularContentTextRendererState;
|
||||
private normalizeProps;
|
||||
}
|
||||
|
||||
export { type AngularContentEllipsisConfig, type AngularContentSnapshot, AngularContentSuggestionsAdapter, type AngularContentTextProps, AngularContentTextRenderer, type AngularContentTextRendererState, type AngularContentTextWithSuggestionsProps, AngularContentTextWithSuggestionsRenderer, type AngularContentTitleWithSuggestionsProps, AngularContentTitleWithSuggestionsRenderer, type AngularContentToken, type AngularCountEllipsisConfig, type AngularEllipsisSymbol, type AngularLinkRenderer, type AngularLinkToken, type AngularMentionRenderer, type AngularMentionToken, type AngularRenderResult, type AngularRowsEllipsisConfig, type AngularTagRenderer, type AngularTagToken, type AngularTextToken, buildAngularTagHref, createAngularContentTokens, toKebabCase };
|
||||
119
packages/content-suggestions/dist/angular/index.d.ts
vendored
119
packages/content-suggestions/dist/angular/index.d.ts
vendored
@@ -1,118 +1 @@
|
||||
import { C as ContentEntity, M as MentionEntity, T as TagEntity, L as LinkEntity } from '../types-BRt4hd7A.js';
|
||||
|
||||
interface AngularTextToken {
|
||||
kind: "text";
|
||||
text: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
interface AngularMentionToken {
|
||||
kind: "mention";
|
||||
entity: MentionEntity;
|
||||
}
|
||||
interface AngularTagToken {
|
||||
kind: "tag";
|
||||
entity: TagEntity;
|
||||
}
|
||||
interface AngularLinkToken {
|
||||
kind: "link";
|
||||
entity: LinkEntity;
|
||||
}
|
||||
type AngularContentToken = AngularTextToken | AngularMentionToken | AngularTagToken | AngularLinkToken;
|
||||
interface AngularContentSnapshot {
|
||||
text: string;
|
||||
entities: ContentEntity[];
|
||||
tokens: AngularContentToken[];
|
||||
}
|
||||
type AngularEllipsisSymbol = string | ((expanded: boolean) => string);
|
||||
type AngularCountEllipsisConfig = {
|
||||
count: number;
|
||||
rows?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: (expanded: boolean) => void;
|
||||
};
|
||||
type AngularRowsEllipsisConfig = {
|
||||
rows: number;
|
||||
count?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: (expanded: boolean) => void;
|
||||
};
|
||||
type AngularContentEllipsisConfig = AngularCountEllipsisConfig | AngularRowsEllipsisConfig | false;
|
||||
type AngularRenderResult = Node | string | number | null | undefined;
|
||||
type AngularMentionRenderer = (entity: MentionEntity, index: number) => AngularRenderResult;
|
||||
type AngularTagRenderer = (entity: TagEntity, index: number) => AngularRenderResult;
|
||||
type AngularLinkRenderer = (entity: LinkEntity, index: number) => AngularRenderResult;
|
||||
interface AngularContentTextProps {
|
||||
className?: string;
|
||||
weight?: "normal" | "bold";
|
||||
text?: string | null;
|
||||
ellipsis?: AngularContentEllipsisConfig;
|
||||
blur?: boolean;
|
||||
style?: Record<string, string | number> | null;
|
||||
onView?: () => void;
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
renderLink?: AngularLinkRenderer;
|
||||
}
|
||||
type AngularContentTextWithSuggestionsProps = Omit<AngularContentTextProps, "renderMention" | "renderTag"> & {
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
};
|
||||
type AngularContentTitleWithSuggestionsProps = Omit<AngularContentTextWithSuggestionsProps, "weight">;
|
||||
interface AngularContentTextRendererState {
|
||||
props: AngularContentTextProps;
|
||||
snapshot: AngularContentSnapshot;
|
||||
expanded: boolean;
|
||||
}
|
||||
declare const toKebabCase: (value: string) => string;
|
||||
declare const buildAngularTagHref: (entity: TagEntity) => string;
|
||||
|
||||
declare const createAngularContentTokens: (inputText: string | null | undefined) => AngularContentToken[];
|
||||
declare class AngularContentSuggestionsAdapter {
|
||||
snapshot(inputText: string | null | undefined): AngularContentSnapshot;
|
||||
}
|
||||
declare class AngularContentTextRenderer {
|
||||
private host;
|
||||
private props;
|
||||
private expanded;
|
||||
private viewed;
|
||||
private hostInsideView;
|
||||
private observer;
|
||||
attach(host: HTMLElement, props?: AngularContentTextProps): AngularContentTextRendererState;
|
||||
update(nextProps: AngularContentTextProps): AngularContentTextRendererState;
|
||||
destroy(): void;
|
||||
getState(): AngularContentTextRendererState;
|
||||
private getText;
|
||||
private createSnapshot;
|
||||
private resolveEllipsisConfig;
|
||||
private getMergedExpanded;
|
||||
private createDefaultMentionNode;
|
||||
private createDefaultTagNode;
|
||||
private createDefaultLinkNode;
|
||||
private renderEntity;
|
||||
private buildTextNodes;
|
||||
private applyBaseParagraphStyle;
|
||||
private buildReadMoreButton;
|
||||
private handleExpand;
|
||||
private computeEntitySafeCutoff;
|
||||
private hasVerticalOverflow;
|
||||
private render;
|
||||
private initObserver;
|
||||
private emitOnViewIfNeeded;
|
||||
}
|
||||
declare class AngularContentTextWithSuggestionsRenderer extends AngularContentTextRenderer {
|
||||
}
|
||||
declare class AngularContentTitleWithSuggestionsRenderer {
|
||||
private readonly renderer;
|
||||
attach(host: HTMLElement, props?: AngularContentTitleWithSuggestionsProps): AngularContentTextRendererState;
|
||||
update(props: AngularContentTitleWithSuggestionsProps): AngularContentTextRendererState;
|
||||
destroy(): void;
|
||||
getState(): AngularContentTextRendererState;
|
||||
private normalizeProps;
|
||||
}
|
||||
|
||||
export { type AngularContentEllipsisConfig, type AngularContentSnapshot, AngularContentSuggestionsAdapter, type AngularContentTextProps, AngularContentTextRenderer, type AngularContentTextRendererState, type AngularContentTextWithSuggestionsProps, AngularContentTextWithSuggestionsRenderer, type AngularContentTitleWithSuggestionsProps, AngularContentTitleWithSuggestionsRenderer, type AngularContentToken, type AngularCountEllipsisConfig, type AngularEllipsisSymbol, type AngularLinkRenderer, type AngularLinkToken, type AngularMentionRenderer, type AngularMentionToken, type AngularRenderResult, type AngularRowsEllipsisConfig, type AngularTagRenderer, type AngularTagToken, type AngularTextToken, buildAngularTagHref, createAngularContentTokens, toKebabCase };
|
||||
export * from "./angular/index";
|
||||
|
||||
488
packages/content-suggestions/dist/angular/index.js
vendored
488
packages/content-suggestions/dist/angular/index.js
vendored
@@ -1,487 +1 @@
|
||||
// src/core/parser.ts
|
||||
var mentionLinkRegexp = /@\[[^\]]+]\([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\)/g;
|
||||
var parseMention = (mention) => {
|
||||
const regex = /@\[([^\]]+)\]\(([\w-]{36})\)/;
|
||||
const match = mention.match(regex);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
const mentionText = match[1];
|
||||
const mentionId = match[2];
|
||||
if (!mentionText || !mentionId) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
mention: `@${mentionText}`,
|
||||
id: mentionId
|
||||
};
|
||||
};
|
||||
var findMentions = (text) => {
|
||||
let match;
|
||||
const matches = [];
|
||||
while ((match = mentionLinkRegexp.exec(text)) !== null) {
|
||||
const parsed = parseMention(match[0]);
|
||||
const baseMention = {
|
||||
start: match.index,
|
||||
end: mentionLinkRegexp.lastIndex,
|
||||
text: match[0],
|
||||
type: "mention",
|
||||
displayText: parsed?.mention ?? match[0]
|
||||
};
|
||||
matches.push(parsed?.id ? { ...baseMention, userId: parsed.id } : baseMention);
|
||||
}
|
||||
return matches;
|
||||
};
|
||||
var findTags = (content) => {
|
||||
const regex = /#[^\s]{1,201}/g;
|
||||
const results = [];
|
||||
let match;
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
const value = match[0];
|
||||
results.push({
|
||||
start: match.index,
|
||||
end: match.index + value.length,
|
||||
text: value,
|
||||
type: "tag",
|
||||
tag: value.replace("#", "")
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
var findLinks = (content) => {
|
||||
const regex = /\b((https?:\/\/)?(?:[\w-]+\.)+[a-z]{2,}(\/[\w\-._~:/?#[\]@!$&'()*+,;=]*)?)/gi;
|
||||
const results = [];
|
||||
let match;
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
const rawUrl = match[0];
|
||||
const hasProtocol = /^https?:\/\//i.test(rawUrl);
|
||||
const fullUrl = hasProtocol ? rawUrl : `https://${rawUrl}`;
|
||||
results.push({
|
||||
start: match.index,
|
||||
end: match.index + rawUrl.length,
|
||||
text: rawUrl,
|
||||
url: fullUrl,
|
||||
type: "link"
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
var findAllEntities = (content) => {
|
||||
const mentions = findMentions(content);
|
||||
const tags = findTags(content);
|
||||
const links = findLinks(content);
|
||||
return [...mentions, ...tags, ...links].sort((a, b) => a.start - b.start);
|
||||
};
|
||||
|
||||
// src/angular/index.ts
|
||||
var LINK_COLOR = "#1677ff";
|
||||
var READ_MORE_TEXT = "\u0427\u0438\u0442\u0430\u0442\u044C \u043F\u043E\u043B\u043D\u043E\u0441\u0442\u044C\u044E";
|
||||
var toKebabCase = (value) => value.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
|
||||
var applyStyleObject = (element, styles) => {
|
||||
if (!styles) {
|
||||
return;
|
||||
}
|
||||
for (const [key, rawValue] of Object.entries(styles)) {
|
||||
if (rawValue === null || rawValue === void 0) {
|
||||
continue;
|
||||
}
|
||||
const styleValue = typeof rawValue === "number" ? `${rawValue}px` : String(rawValue);
|
||||
if (key.startsWith("--")) {
|
||||
element.style.setProperty(key, styleValue);
|
||||
continue;
|
||||
}
|
||||
const cssKey = key.includes("-") ? key : toKebabCase(key);
|
||||
element.style.setProperty(cssKey, styleValue);
|
||||
}
|
||||
};
|
||||
var toNode = (value) => {
|
||||
if (value === null || value === void 0) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Node) {
|
||||
return value;
|
||||
}
|
||||
return document.createTextNode(String(value));
|
||||
};
|
||||
var resolveEllipsisSymbol = (symbol, expanded) => {
|
||||
if (typeof symbol === "function") {
|
||||
return symbol(expanded);
|
||||
}
|
||||
return symbol ?? READ_MORE_TEXT;
|
||||
};
|
||||
var buildAngularTagHref = (entity) => {
|
||||
return `/search/?query=${encodeURIComponent(entity.tag.toLowerCase())}`;
|
||||
};
|
||||
var createAngularContentTokens = (inputText) => {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
let cursor = 0;
|
||||
const tokens = [];
|
||||
for (const entity of entities) {
|
||||
if (entity.start > cursor) {
|
||||
tokens.push({
|
||||
kind: "text",
|
||||
text: text.slice(cursor, entity.start),
|
||||
start: cursor,
|
||||
end: entity.start
|
||||
});
|
||||
}
|
||||
if (entity.type === "mention") {
|
||||
tokens.push({
|
||||
kind: "mention",
|
||||
entity
|
||||
});
|
||||
} else if (entity.type === "tag") {
|
||||
tokens.push({
|
||||
kind: "tag",
|
||||
entity
|
||||
});
|
||||
} else {
|
||||
tokens.push({
|
||||
kind: "link",
|
||||
entity
|
||||
});
|
||||
}
|
||||
cursor = entity.end;
|
||||
}
|
||||
if (cursor < text.length) {
|
||||
tokens.push({
|
||||
kind: "text",
|
||||
text: text.slice(cursor),
|
||||
start: cursor,
|
||||
end: text.length
|
||||
});
|
||||
}
|
||||
return tokens;
|
||||
};
|
||||
var AngularContentSuggestionsAdapter = class {
|
||||
snapshot(inputText) {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
const tokens = createAngularContentTokens(text);
|
||||
return {
|
||||
text,
|
||||
entities,
|
||||
tokens
|
||||
};
|
||||
}
|
||||
};
|
||||
var AngularContentTextRenderer = class {
|
||||
host = null;
|
||||
props = {};
|
||||
expanded = false;
|
||||
viewed = false;
|
||||
hostInsideView = false;
|
||||
observer = null;
|
||||
attach(host, props = {}) {
|
||||
this.destroy();
|
||||
this.host = host;
|
||||
this.props = { ...props };
|
||||
this.expanded = false;
|
||||
this.viewed = false;
|
||||
this.hostInsideView = false;
|
||||
this.initObserver();
|
||||
this.render();
|
||||
return this.getState();
|
||||
}
|
||||
update(nextProps) {
|
||||
this.props = {
|
||||
...this.props,
|
||||
...nextProps
|
||||
};
|
||||
this.render();
|
||||
return this.getState();
|
||||
}
|
||||
destroy() {
|
||||
if (this.observer && this.host) {
|
||||
this.observer.unobserve(this.host);
|
||||
this.observer.disconnect();
|
||||
}
|
||||
this.observer = null;
|
||||
if (this.host) {
|
||||
this.host.innerHTML = "";
|
||||
}
|
||||
this.host = null;
|
||||
this.props = {};
|
||||
this.expanded = false;
|
||||
this.viewed = false;
|
||||
this.hostInsideView = false;
|
||||
}
|
||||
getState() {
|
||||
return {
|
||||
props: { ...this.props },
|
||||
snapshot: this.createSnapshot(),
|
||||
expanded: this.getMergedExpanded(this.resolveEllipsisConfig(), this.getText())
|
||||
};
|
||||
}
|
||||
getText() {
|
||||
return this.props.text ?? "";
|
||||
}
|
||||
createSnapshot() {
|
||||
const text = this.getText();
|
||||
const entities = findAllEntities(text);
|
||||
const tokens = createAngularContentTokens(text);
|
||||
return {
|
||||
text,
|
||||
entities,
|
||||
tokens
|
||||
};
|
||||
}
|
||||
resolveEllipsisConfig() {
|
||||
const ellipsis = this.props.ellipsis;
|
||||
if (!ellipsis || typeof ellipsis !== "object") {
|
||||
return null;
|
||||
}
|
||||
return ellipsis;
|
||||
}
|
||||
getMergedExpanded(ellipsisConfig, text) {
|
||||
const controlledExpanded = ellipsisConfig && typeof ellipsisConfig.expanded === "boolean" ? ellipsisConfig.expanded : void 0;
|
||||
if (controlledExpanded !== void 0) {
|
||||
return controlledExpanded;
|
||||
}
|
||||
if (ellipsisConfig && "count" in ellipsisConfig) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
if (count <= 0 || text.length <= count) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return this.expanded;
|
||||
}
|
||||
createDefaultMentionNode(entity) {
|
||||
const span = document.createElement("span");
|
||||
span.style.color = LINK_COLOR;
|
||||
span.style.fontWeight = this.props.weight === "bold" ? "700" : "400";
|
||||
span.textContent = entity.displayText;
|
||||
return span;
|
||||
}
|
||||
createDefaultTagNode(entity) {
|
||||
const span = document.createElement("span");
|
||||
span.style.color = LINK_COLOR;
|
||||
span.style.fontWeight = this.props.weight === "bold" ? "700" : "400";
|
||||
span.textContent = entity.text;
|
||||
return span;
|
||||
}
|
||||
createDefaultLinkNode(entity) {
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = entity.url;
|
||||
anchor.target = "_blank";
|
||||
anchor.referrerPolicy = "no-referrer";
|
||||
anchor.style.color = LINK_COLOR;
|
||||
anchor.style.fontWeight = this.props.weight === "bold" ? "700" : "400";
|
||||
anchor.textContent = entity.text;
|
||||
return anchor;
|
||||
}
|
||||
renderEntity(entity, index) {
|
||||
if (entity.type === "mention") {
|
||||
const customNode2 = this.props.renderMention?.(entity, index);
|
||||
return toNode(customNode2) ?? this.createDefaultMentionNode(entity);
|
||||
}
|
||||
if (entity.type === "tag") {
|
||||
const customNode2 = this.props.renderTag?.(entity, index);
|
||||
return toNode(customNode2) ?? this.createDefaultTagNode(entity);
|
||||
}
|
||||
const customNode = this.props.renderLink?.(entity, index);
|
||||
return toNode(customNode) ?? this.createDefaultLinkNode(entity);
|
||||
}
|
||||
buildTextNodes(text, entities, upto = null) {
|
||||
let lastIndex = 0;
|
||||
const nodes = [];
|
||||
for (const [index, entity] of entities.entries()) {
|
||||
if (upto !== null && entity.start >= upto) {
|
||||
break;
|
||||
}
|
||||
const textEnd = upto !== null ? Math.min(entity.start, upto) : entity.start;
|
||||
if (entity.start > lastIndex && lastIndex < textEnd) {
|
||||
nodes.push(document.createTextNode(text.slice(lastIndex, textEnd)));
|
||||
}
|
||||
if (upto === null || entity.end <= upto) {
|
||||
const entityNode = this.renderEntity(entity, index);
|
||||
if (entityNode) {
|
||||
nodes.push(entityNode);
|
||||
}
|
||||
}
|
||||
lastIndex = entity.end;
|
||||
}
|
||||
if (upto === null) {
|
||||
if (lastIndex < text.length) {
|
||||
nodes.push(document.createTextNode(text.slice(lastIndex)));
|
||||
}
|
||||
} else if (lastIndex < upto) {
|
||||
nodes.push(document.createTextNode(text.slice(lastIndex, upto)));
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
applyBaseParagraphStyle(element) {
|
||||
element.style.whiteSpace = "pre-wrap";
|
||||
element.style.fontWeight = this.props.weight === "bold" ? "700" : "400";
|
||||
element.style.setProperty("-webkit-touch-callout", "default");
|
||||
element.style.setProperty("-webkit-user-select", "text");
|
||||
element.style.setProperty("-khtml-user-select", "text");
|
||||
element.style.setProperty("-moz-user-select", "text");
|
||||
element.style.setProperty("-ms-user-select", "text");
|
||||
element.style.setProperty("user-select", "text");
|
||||
if (this.props.blur) {
|
||||
element.style.filter = "blur(3px)";
|
||||
element.style.setProperty("-webkit-user-select", "none");
|
||||
element.style.setProperty("-khtml-user-select", "none");
|
||||
element.style.setProperty("-moz-user-select", "none");
|
||||
element.style.setProperty("-ms-user-select", "none");
|
||||
element.style.setProperty("user-select", "none");
|
||||
element.style.pointerEvents = "none";
|
||||
}
|
||||
applyStyleObject(element, this.props.style);
|
||||
}
|
||||
buildReadMoreButton(symbol) {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.textContent = resolveEllipsisSymbol(symbol, this.expanded);
|
||||
button.style.border = "0";
|
||||
button.style.background = "none";
|
||||
button.style.padding = "0";
|
||||
button.style.marginLeft = "6px";
|
||||
button.style.cursor = "pointer";
|
||||
button.style.color = LINK_COLOR;
|
||||
button.style.fontWeight = "700";
|
||||
button.onclick = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.handleExpand();
|
||||
};
|
||||
return button;
|
||||
}
|
||||
handleExpand() {
|
||||
const ellipsisConfig = this.resolveEllipsisConfig();
|
||||
if (!ellipsisConfig) {
|
||||
return;
|
||||
}
|
||||
this.expanded = true;
|
||||
ellipsisConfig.onExpand?.(true);
|
||||
this.render();
|
||||
}
|
||||
computeEntitySafeCutoff(count, entities) {
|
||||
let cutoff = count;
|
||||
let extended = true;
|
||||
while (extended) {
|
||||
extended = false;
|
||||
for (const entity of entities) {
|
||||
if (entity.start < cutoff && entity.end > cutoff) {
|
||||
cutoff = entity.end;
|
||||
extended = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cutoff;
|
||||
}
|
||||
hasVerticalOverflow(element) {
|
||||
return element.scrollHeight - element.clientHeight > 1;
|
||||
}
|
||||
render() {
|
||||
if (!this.host) {
|
||||
return;
|
||||
}
|
||||
const text = this.getText();
|
||||
const entities = findAllEntities(text);
|
||||
const ellipsisConfig = this.resolveEllipsisConfig();
|
||||
const mergedExpanded = this.getMergedExpanded(ellipsisConfig, text);
|
||||
const wrapper = document.createElement("div");
|
||||
const paragraph = document.createElement("div");
|
||||
if (this.props.className) {
|
||||
paragraph.className = this.props.className;
|
||||
}
|
||||
this.applyBaseParagraphStyle(paragraph);
|
||||
if (ellipsisConfig && "count" in ellipsisConfig) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
const shouldTruncate = !mergedExpanded && count > 0 && text.length > count;
|
||||
if (shouldTruncate) {
|
||||
const cutoff = this.computeEntitySafeCutoff(count, entities);
|
||||
const nodes = this.buildTextNodes(text, entities, cutoff);
|
||||
paragraph.append(...nodes);
|
||||
paragraph.append(document.createTextNode("\u2026"));
|
||||
if (ellipsisConfig.expandable) {
|
||||
paragraph.append(this.buildReadMoreButton(ellipsisConfig.symbol));
|
||||
}
|
||||
} else {
|
||||
paragraph.append(...this.buildTextNodes(text, entities));
|
||||
}
|
||||
} else {
|
||||
paragraph.append(...this.buildTextNodes(text, entities));
|
||||
if (ellipsisConfig && "rows" in ellipsisConfig && !mergedExpanded) {
|
||||
paragraph.style.display = "-webkit-box";
|
||||
paragraph.style.setProperty("-webkit-box-orient", "vertical");
|
||||
paragraph.style.setProperty("-webkit-line-clamp", String(ellipsisConfig.rows));
|
||||
paragraph.style.overflow = "hidden";
|
||||
wrapper.append(paragraph);
|
||||
this.host.replaceChildren(wrapper);
|
||||
if (ellipsisConfig.expandable && this.hasVerticalOverflow(paragraph)) {
|
||||
wrapper.append(this.buildReadMoreButton(ellipsisConfig.symbol));
|
||||
}
|
||||
this.emitOnViewIfNeeded(mergedExpanded);
|
||||
return;
|
||||
}
|
||||
}
|
||||
wrapper.append(paragraph);
|
||||
this.host.replaceChildren(wrapper);
|
||||
this.emitOnViewIfNeeded(mergedExpanded);
|
||||
}
|
||||
initObserver() {
|
||||
if (!this.host) {
|
||||
return;
|
||||
}
|
||||
if (typeof IntersectionObserver === "undefined") {
|
||||
this.hostInsideView = true;
|
||||
return;
|
||||
}
|
||||
this.observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
const entry = entries[0];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
if (entry.isIntersecting && !this.hostInsideView) {
|
||||
this.hostInsideView = true;
|
||||
const ellipsisConfig = this.resolveEllipsisConfig();
|
||||
const mergedExpanded = this.getMergedExpanded(ellipsisConfig, this.getText());
|
||||
this.emitOnViewIfNeeded(mergedExpanded);
|
||||
}
|
||||
},
|
||||
{ threshold: 0.5 }
|
||||
);
|
||||
this.observer.observe(this.host);
|
||||
}
|
||||
emitOnViewIfNeeded(mergedExpanded) {
|
||||
if (!mergedExpanded || this.viewed || !this.hostInsideView) {
|
||||
return;
|
||||
}
|
||||
this.viewed = true;
|
||||
this.props.onView?.();
|
||||
}
|
||||
};
|
||||
var AngularContentTextWithSuggestionsRenderer = class extends AngularContentTextRenderer {
|
||||
};
|
||||
var AngularContentTitleWithSuggestionsRenderer = class {
|
||||
renderer = new AngularContentTextWithSuggestionsRenderer();
|
||||
attach(host, props = {}) {
|
||||
return this.renderer.attach(host, this.normalizeProps(props));
|
||||
}
|
||||
update(props) {
|
||||
return this.renderer.update(this.normalizeProps(props));
|
||||
}
|
||||
destroy() {
|
||||
this.renderer.destroy();
|
||||
}
|
||||
getState() {
|
||||
return this.renderer.getState();
|
||||
}
|
||||
normalizeProps(props) {
|
||||
return {
|
||||
...props,
|
||||
weight: "bold",
|
||||
blur: props.blur ?? false,
|
||||
ellipsis: props.ellipsis === void 0 ? { rows: 2 } : props.ellipsis
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export { AngularContentSuggestionsAdapter, AngularContentTextRenderer, AngularContentTextWithSuggestionsRenderer, AngularContentTitleWithSuggestionsRenderer, buildAngularTagHref, createAngularContentTokens, toKebabCase };
|
||||
//# sourceMappingURL=index.js.map
|
||||
//# sourceMappingURL=index.js.map
|
||||
export * from "./angular/index.js";
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,30 @@
|
||||
import { C as ContentEntity, L as LinkEntity, M as MentionEntity, T as TagEntity, P as ParsedMention, a as ProcessedContent } from '../types-BRt4hd7A.cjs';
|
||||
export { B as BaseEntity } from '../types-BRt4hd7A.cjs';
|
||||
interface BaseEntity {
|
||||
start: number;
|
||||
end: number;
|
||||
text: string;
|
||||
}
|
||||
interface MentionEntity extends BaseEntity {
|
||||
type: "mention";
|
||||
displayText: string;
|
||||
userId?: string;
|
||||
}
|
||||
interface TagEntity extends BaseEntity {
|
||||
type: "tag";
|
||||
tag: string;
|
||||
}
|
||||
interface LinkEntity extends BaseEntity {
|
||||
type: "link";
|
||||
url: string;
|
||||
}
|
||||
type ContentEntity = MentionEntity | TagEntity | LinkEntity;
|
||||
interface ParsedMention {
|
||||
mention: string;
|
||||
id: string;
|
||||
}
|
||||
interface ProcessedContent {
|
||||
processedText: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
declare const mentionLinkRegexp: RegExp;
|
||||
declare const parseMention: (mention: string) => ParsedMention | null;
|
||||
@@ -9,4 +34,4 @@ declare const findLinks: (content: string) => LinkEntity[];
|
||||
declare const findAllEntities: (content: string) => ContentEntity[];
|
||||
declare const processContent: (content: string) => ProcessedContent;
|
||||
|
||||
export { ContentEntity, LinkEntity, MentionEntity, ParsedMention, ProcessedContent, TagEntity, findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent };
|
||||
export { type BaseEntity, type ContentEntity, type LinkEntity, type MentionEntity, type ParsedMention, type ProcessedContent, type TagEntity, findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent };
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
import { C as ContentEntity, L as LinkEntity, M as MentionEntity, T as TagEntity, P as ParsedMention, a as ProcessedContent } from '../types-BRt4hd7A.js';
|
||||
export { B as BaseEntity } from '../types-BRt4hd7A.js';
|
||||
interface BaseEntity {
|
||||
start: number;
|
||||
end: number;
|
||||
text: string;
|
||||
}
|
||||
interface MentionEntity extends BaseEntity {
|
||||
type: "mention";
|
||||
displayText: string;
|
||||
userId?: string;
|
||||
}
|
||||
interface TagEntity extends BaseEntity {
|
||||
type: "tag";
|
||||
tag: string;
|
||||
}
|
||||
interface LinkEntity extends BaseEntity {
|
||||
type: "link";
|
||||
url: string;
|
||||
}
|
||||
type ContentEntity = MentionEntity | TagEntity | LinkEntity;
|
||||
interface ParsedMention {
|
||||
mention: string;
|
||||
id: string;
|
||||
}
|
||||
interface ProcessedContent {
|
||||
processedText: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
declare const mentionLinkRegexp: RegExp;
|
||||
declare const parseMention: (mention: string) => ParsedMention | null;
|
||||
@@ -9,4 +34,4 @@ declare const findLinks: (content: string) => LinkEntity[];
|
||||
declare const findAllEntities: (content: string) => ContentEntity[];
|
||||
declare const processContent: (content: string) => ProcessedContent;
|
||||
|
||||
export { ContentEntity, LinkEntity, MentionEntity, ParsedMention, ProcessedContent, TagEntity, findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent };
|
||||
export { type BaseEntity, type ContentEntity, type LinkEntity, type MentionEntity, type ParsedMention, type ProcessedContent, type TagEntity, findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent };
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,10 +1,9 @@
|
||||
import React, { ComponentProps, ReactNode } from 'react';
|
||||
import { EllipsisConfig } from 'antd/lib/typography/Base';
|
||||
import { M as MentionEntity, T as TagEntity, L as LinkEntity } from '../types-BRt4hd7A.cjs';
|
||||
export { B as BaseEntity, C as ContentEntity, P as ParsedMention, a as ProcessedContent } from '../types-BRt4hd7A.cjs';
|
||||
import { MentionEntity, TagEntity, LinkEntity } from '../core/index.cjs';
|
||||
export { BaseEntity, ContentEntity, ParsedMention, ProcessedContent, findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent } from '../core/index.cjs';
|
||||
import { TachTypography } from '@hublib-web/tach-typography/react';
|
||||
import * as react_jsx_runtime from 'react/jsx-runtime';
|
||||
export { findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent } from '../core/index.cjs';
|
||||
|
||||
type CustomEllipsisConfig = ({
|
||||
count: number;
|
||||
@@ -41,8 +40,7 @@ type BaseContentTextProps = Omit<ComponentProps<typeof ContentText>, "renderMent
|
||||
};
|
||||
declare const ContentTextWithSuggestions: ({ renderMention, renderTag, ...props }: BaseContentTextProps) => react_jsx_runtime.JSX.Element;
|
||||
|
||||
interface ContentTitleWithSuggestionsProps extends Omit<ComponentProps<typeof ContentTextWithSuggestions>, "weight"> {
|
||||
}
|
||||
type ContentTitleWithSuggestionsProps = Omit<ComponentProps<typeof ContentTextWithSuggestions>, "weight">;
|
||||
declare const ContentTitleWithSuggestions: ({ text, ellipsis, blur, ...rest }: ContentTitleWithSuggestionsProps) => react_jsx_runtime.JSX.Element;
|
||||
|
||||
export { ContentText, type ContentTextProps, ContentTextWithSuggestions, ContentTitleWithSuggestions, LinkEntity, MentionEntity, TagEntity };
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React, { ComponentProps, ReactNode } from 'react';
|
||||
import { EllipsisConfig } from 'antd/lib/typography/Base';
|
||||
import { M as MentionEntity, T as TagEntity, L as LinkEntity } from '../types-BRt4hd7A.js';
|
||||
export { B as BaseEntity, C as ContentEntity, P as ParsedMention, a as ProcessedContent } from '../types-BRt4hd7A.js';
|
||||
import { MentionEntity, TagEntity, LinkEntity } from '../core/index.js';
|
||||
export { BaseEntity, ContentEntity, ParsedMention, ProcessedContent, findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent } from '../core/index.js';
|
||||
import { TachTypography } from '@hublib-web/tach-typography/react';
|
||||
import * as react_jsx_runtime from 'react/jsx-runtime';
|
||||
export { findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent } from '../core/index.js';
|
||||
|
||||
type CustomEllipsisConfig = ({
|
||||
count: number;
|
||||
@@ -41,8 +40,7 @@ type BaseContentTextProps = Omit<ComponentProps<typeof ContentText>, "renderMent
|
||||
};
|
||||
declare const ContentTextWithSuggestions: ({ renderMention, renderTag, ...props }: BaseContentTextProps) => react_jsx_runtime.JSX.Element;
|
||||
|
||||
interface ContentTitleWithSuggestionsProps extends Omit<ComponentProps<typeof ContentTextWithSuggestions>, "weight"> {
|
||||
}
|
||||
type ContentTitleWithSuggestionsProps = Omit<ComponentProps<typeof ContentTextWithSuggestions>, "weight">;
|
||||
declare const ContentTitleWithSuggestions: ({ text, ellipsis, blur, ...rest }: ContentTitleWithSuggestionsProps) => react_jsx_runtime.JSX.Element;
|
||||
|
||||
export { ContentText, type ContentTextProps, ContentTextWithSuggestions, ContentTitleWithSuggestions, LinkEntity, MentionEntity, TagEntity };
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,29 +0,0 @@
|
||||
interface BaseEntity {
|
||||
start: number;
|
||||
end: number;
|
||||
text: string;
|
||||
}
|
||||
interface MentionEntity extends BaseEntity {
|
||||
type: "mention";
|
||||
displayText: string;
|
||||
userId?: string;
|
||||
}
|
||||
interface TagEntity extends BaseEntity {
|
||||
type: "tag";
|
||||
tag: string;
|
||||
}
|
||||
interface LinkEntity extends BaseEntity {
|
||||
type: "link";
|
||||
url: string;
|
||||
}
|
||||
type ContentEntity = MentionEntity | TagEntity | LinkEntity;
|
||||
interface ParsedMention {
|
||||
mention: string;
|
||||
id: string;
|
||||
}
|
||||
interface ProcessedContent {
|
||||
processedText: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export type { BaseEntity as B, ContentEntity as C, LinkEntity as L, MentionEntity as M, ParsedMention as P, TagEntity as T, ProcessedContent as a };
|
||||
@@ -1,29 +0,0 @@
|
||||
interface BaseEntity {
|
||||
start: number;
|
||||
end: number;
|
||||
text: string;
|
||||
}
|
||||
interface MentionEntity extends BaseEntity {
|
||||
type: "mention";
|
||||
displayText: string;
|
||||
userId?: string;
|
||||
}
|
||||
interface TagEntity extends BaseEntity {
|
||||
type: "tag";
|
||||
tag: string;
|
||||
}
|
||||
interface LinkEntity extends BaseEntity {
|
||||
type: "link";
|
||||
url: string;
|
||||
}
|
||||
type ContentEntity = MentionEntity | TagEntity | LinkEntity;
|
||||
interface ParsedMention {
|
||||
mention: string;
|
||||
id: string;
|
||||
}
|
||||
interface ProcessedContent {
|
||||
processedText: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export type { BaseEntity as B, ContentEntity as C, LinkEntity as L, MentionEntity as M, ParsedMention as P, TagEntity as T, ProcessedContent as a };
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hublib-web/content-suggestions",
|
||||
"version": "0.1.2",
|
||||
"version": "0.2.0",
|
||||
"description": "Content text/title with mentions, tags and links for React and Angular",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
@@ -43,12 +43,12 @@
|
||||
},
|
||||
"./angular": {
|
||||
"types": "./dist/angular/index.d.ts",
|
||||
"import": "./dist/angular/index.js",
|
||||
"require": "./dist/angular/index.cjs"
|
||||
"import": "./dist/angular/index.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn clean && tsup",
|
||||
"build": "yarn clean && tsup && yarn build:angular",
|
||||
"build:angular": "ngc -p tsconfig.angular.json && node ./scripts/fix-angular-entry.mjs",
|
||||
"clean": "rm -rf dist storybook-static",
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"test": "vitest run --passWithNoTests",
|
||||
@@ -59,7 +59,7 @@
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">=17.0.0",
|
||||
"@angular/core": ">=17.0.0",
|
||||
"@hublib-web/tach-typography": ">=0.1.0",
|
||||
"@hublib-web/tach-typography": "0.3.0",
|
||||
"antd": ">=5.0.0",
|
||||
"react": ">=18.0.0",
|
||||
"react-dom": ">=18.0.0",
|
||||
@@ -87,8 +87,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/common": "^20.3.17",
|
||||
"@angular/compiler": "^20.3.17",
|
||||
"@angular/compiler-cli": "^20.3.17",
|
||||
"@angular/core": "^20.3.17",
|
||||
"@hublib-web/tach-typography": "workspace:*",
|
||||
"@hublib-web/tach-typography": "workspace:0.3.0",
|
||||
"@storybook/addon-essentials": "8.6.14",
|
||||
"@storybook/react": "8.6.14",
|
||||
"@storybook/react-vite": "8.6.14",
|
||||
|
||||
12
packages/content-suggestions/scripts/fix-angular-entry.mjs
Normal file
12
packages/content-suggestions/scripts/fix-angular-entry.mjs
Normal file
@@ -0,0 +1,12 @@
|
||||
import { access, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
const distAngularDir = path.resolve("dist/angular");
|
||||
await access(path.join(distAngularDir, "angular/index.js"));
|
||||
await access(path.join(distAngularDir, "angular/index.d.ts"));
|
||||
|
||||
await writeFile(
|
||||
path.join(distAngularDir, "index.js"),
|
||||
'export * from "./angular/index.js";\n',
|
||||
);
|
||||
await writeFile(path.join(distAngularDir, "index.d.ts"), 'export * from "./angular/index";\n');
|
||||
@@ -0,0 +1,50 @@
|
||||
import type {
|
||||
AngularContentEllipsisConfig,
|
||||
AngularLinkRenderer,
|
||||
AngularMentionRenderer,
|
||||
AngularTagRenderer,
|
||||
} from "./types";
|
||||
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
|
||||
import type { TypographyWeight } from "@hublib-web/tach-typography/core";
|
||||
|
||||
import { ContentTextComponent } from "./content-text.component";
|
||||
|
||||
@Component({
|
||||
selector: "content-text-with-suggestions",
|
||||
standalone: true,
|
||||
imports: [ContentTextComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<content-text
|
||||
[className]="className"
|
||||
[weight]="weight"
|
||||
[text]="text"
|
||||
[ellipsis]="ellipsis"
|
||||
[blur]="blur"
|
||||
[style]="style"
|
||||
[onView]="onView"
|
||||
[renderMention]="renderMention"
|
||||
[renderTag]="renderTag"
|
||||
[renderLink]="renderLink"
|
||||
(viewed)="viewed.emit()"
|
||||
(expandedChange)="expandedChange.emit($event)"
|
||||
/>
|
||||
`,
|
||||
})
|
||||
export class ContentTextWithSuggestionsComponent {
|
||||
@Input() className: string | undefined;
|
||||
@Input() weight: TypographyWeight = "normal";
|
||||
@Input() text: string | null | undefined;
|
||||
@Input() ellipsis: AngularContentEllipsisConfig = false;
|
||||
@Input() blur = false;
|
||||
@Input() style: Record<string, string | number> | null | undefined;
|
||||
@Input() onView: (() => void) | undefined;
|
||||
@Input() renderMention: AngularMentionRenderer | undefined;
|
||||
@Input() renderTag: AngularTagRenderer | undefined;
|
||||
@Input() renderLink: AngularLinkRenderer | undefined;
|
||||
|
||||
@Output() readonly viewed = new EventEmitter<void>();
|
||||
@Output() readonly expandedChange = new EventEmitter<boolean>();
|
||||
}
|
||||
@@ -0,0 +1,571 @@
|
||||
import type { LinkEntity, MentionEntity, TagEntity } from "../core";
|
||||
import type {
|
||||
AngularContentEllipsisConfig,
|
||||
AngularCountEllipsisConfig,
|
||||
AngularLinkRenderer,
|
||||
AngularRenderResult,
|
||||
AngularRowsEllipsisConfig,
|
||||
AngularTagRenderer,
|
||||
AngularMentionRenderer,
|
||||
} from "./types";
|
||||
|
||||
import { NgFor, NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault } from "@angular/common";
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
Output,
|
||||
SimpleChanges,
|
||||
ViewChild,
|
||||
} from "@angular/core";
|
||||
|
||||
import {
|
||||
TachTypographyComponent,
|
||||
type TachTypographyHostProps,
|
||||
} from "@hublib-web/tach-typography/angular";
|
||||
import type { TypographyWeight } from "@hublib-web/tach-typography/core";
|
||||
|
||||
import { findAllEntities } from "../core";
|
||||
import { ContentSuggestionsRenderResultDirective } from "./render-result.directive";
|
||||
|
||||
const READ_MORE_TEXT = "Читать полностью";
|
||||
const BODY_DATA_ATTR = "data-content-suggestions-body";
|
||||
|
||||
const BASE_SELECTABLE_STYLE: Record<string, string> = {
|
||||
whiteSpace: "pre-wrap",
|
||||
WebkitTouchCallout: "default",
|
||||
WebkitUserSelect: "text",
|
||||
KhtmlUserSelect: "text",
|
||||
MozUserSelect: "text",
|
||||
msUserSelect: "text",
|
||||
userSelect: "text",
|
||||
};
|
||||
|
||||
const BLUR_STYLE: Record<string, string> = {
|
||||
filter: "blur(3px)",
|
||||
WebkitUserSelect: "none",
|
||||
KhtmlUserSelect: "none",
|
||||
MozUserSelect: "none",
|
||||
msUserSelect: "none",
|
||||
userSelect: "none",
|
||||
pointerEvents: "none",
|
||||
};
|
||||
|
||||
type TextPart = {
|
||||
kind: "text";
|
||||
key: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
type MentionPart = {
|
||||
kind: "mention";
|
||||
key: string;
|
||||
entity: MentionEntity;
|
||||
index: number;
|
||||
customRender: AngularRenderResult | null;
|
||||
};
|
||||
|
||||
type TagPart = {
|
||||
kind: "tag";
|
||||
key: string;
|
||||
entity: TagEntity;
|
||||
index: number;
|
||||
customRender: AngularRenderResult | null;
|
||||
};
|
||||
|
||||
type LinkPart = {
|
||||
kind: "link";
|
||||
key: string;
|
||||
entity: LinkEntity;
|
||||
index: number;
|
||||
customRender: AngularRenderResult | null;
|
||||
};
|
||||
|
||||
type ContentPart = TextPart | MentionPart | TagPart | LinkPart;
|
||||
|
||||
@Component({
|
||||
selector: "content-text",
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgFor,
|
||||
NgIf,
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
NgSwitchDefault,
|
||||
TachTypographyComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<div #container>
|
||||
<tach-typography
|
||||
as="p"
|
||||
variant="Body"
|
||||
[weight]="weight"
|
||||
[className]="className"
|
||||
[preserveStyle]="mergedStyle"
|
||||
[ellipsis]="rowsEllipsis"
|
||||
[hostProps]="paragraphHostProps"
|
||||
>
|
||||
<ng-container *ngFor="let part of visibleParts; trackBy: trackByKey">
|
||||
<ng-container [ngSwitch]="part.kind">
|
||||
<ng-container *ngSwitchCase="'text'">{{ part.text }}</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'mention'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultMention">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultMention>
|
||||
<tach-typography as="span" variant="Body" color="link" [weight]="weight">
|
||||
{{ part.entity.displayText }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'tag'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultTag">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultTag>
|
||||
<tach-typography as="span" variant="Body" color="link" [weight]="weight">
|
||||
{{ part.entity.text }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'link'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultLink">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultLink>
|
||||
<tach-typography
|
||||
as="a"
|
||||
variant="Body"
|
||||
color="link"
|
||||
[weight]="weight"
|
||||
[hostProps]="buildDefaultLinkHostProps(part.entity.url)"
|
||||
>
|
||||
{{ part.entity.text }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchDefault></ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="showTrailingEllipsis">…</ng-container>
|
||||
|
||||
<button
|
||||
*ngIf="showInlineReadMore"
|
||||
type="button"
|
||||
(click)="handleExpand($event)"
|
||||
style="border: 0; background: none; padding: 0; margin-left: 6px; cursor: pointer;"
|
||||
>
|
||||
<tach-typography as="span" variant="Body" color="link" weight="bold">
|
||||
{{ readMoreLabel }}
|
||||
</tach-typography>
|
||||
</button>
|
||||
</tach-typography>
|
||||
|
||||
<button
|
||||
*ngIf="showBlockReadMore"
|
||||
type="button"
|
||||
(click)="handleExpand($event)"
|
||||
style="border: 0; background: none; padding: 0; margin-top: 6px; cursor: pointer;"
|
||||
>
|
||||
<tach-typography as="span" variant="Body" color="link" weight="bold">
|
||||
{{ readMoreLabel }}
|
||||
</tach-typography>
|
||||
</button>
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class ContentTextComponent implements OnChanges, AfterViewInit, OnDestroy {
|
||||
@Input() className: string | undefined;
|
||||
@Input() weight: TypographyWeight = "normal";
|
||||
@Input() text: string | null | undefined;
|
||||
@Input() ellipsis: AngularContentEllipsisConfig = false;
|
||||
@Input() blur = false;
|
||||
@Input() style: Record<string, string | number> | null | undefined;
|
||||
@Input() onView: (() => void) | undefined;
|
||||
@Input() renderMention: AngularMentionRenderer | undefined;
|
||||
@Input() renderTag: AngularTagRenderer | undefined;
|
||||
@Input() renderLink: AngularLinkRenderer | undefined;
|
||||
|
||||
@Output() readonly viewed = new EventEmitter<void>();
|
||||
@Output() readonly expandedChange = new EventEmitter<boolean>();
|
||||
|
||||
@ViewChild("container", { static: true })
|
||||
private readonly containerRef!: ElementRef<HTMLElement>;
|
||||
|
||||
visibleParts: ContentPart[] = [];
|
||||
mergedStyle: Record<string, string | number> = { ...BASE_SELECTABLE_STYLE };
|
||||
rowsEllipsis: { rows: number } | undefined;
|
||||
showTrailingEllipsis = false;
|
||||
showInlineReadMore = false;
|
||||
showBlockReadMore = false;
|
||||
readMoreLabel = READ_MORE_TEXT;
|
||||
paragraphHostProps: TachTypographyHostProps = { [BODY_DATA_ATTR]: "true" };
|
||||
|
||||
private expanded = false;
|
||||
private mergedExpanded = false;
|
||||
private isExpandedControlled = false;
|
||||
private isInsideView = false;
|
||||
private viewedFired = false;
|
||||
private pendingRowsOverflowCheck = false;
|
||||
private intersectionObserver: IntersectionObserver | null = null;
|
||||
|
||||
constructor(
|
||||
private readonly cdr: ChangeDetectorRef,
|
||||
) {}
|
||||
|
||||
ngOnChanges(_changes: SimpleChanges): void {
|
||||
this.recomputeContent();
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.initObserver();
|
||||
this.scheduleRowsOverflowCheck();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.intersectionObserver) {
|
||||
this.intersectionObserver.disconnect();
|
||||
this.intersectionObserver = null;
|
||||
}
|
||||
}
|
||||
|
||||
trackByKey(_index: number, part: ContentPart): string {
|
||||
return part.key;
|
||||
}
|
||||
|
||||
buildDefaultLinkHostProps(url: string): TachTypographyHostProps {
|
||||
return {
|
||||
href: url,
|
||||
target: "_blank",
|
||||
referrerPolicy: "no-referrer",
|
||||
};
|
||||
}
|
||||
|
||||
handleExpand(event: MouseEvent): void {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (!this.isExpandedControlled) {
|
||||
this.expanded = true;
|
||||
}
|
||||
|
||||
this.notifyExpand(event, true);
|
||||
this.recomputeContent();
|
||||
}
|
||||
|
||||
private recomputeContent(): void {
|
||||
const content = this.text ?? "";
|
||||
const entities = findAllEntities(content);
|
||||
const ellipsisConfig = this.resolveEllipsisConfig();
|
||||
|
||||
this.isExpandedControlled = Boolean(
|
||||
ellipsisConfig && typeof ellipsisConfig.expanded === "boolean",
|
||||
);
|
||||
|
||||
if (!this.isExpandedControlled && this.isCountConfig(ellipsisConfig)) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
if (content.length <= count && !this.expanded) {
|
||||
this.expanded = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.mergedExpanded = this.getMergedExpanded(ellipsisConfig, content);
|
||||
this.readMoreLabel = this.resolveEllipsisSymbol(
|
||||
ellipsisConfig?.symbol,
|
||||
this.mergedExpanded,
|
||||
);
|
||||
|
||||
this.mergedStyle = {
|
||||
...BASE_SELECTABLE_STYLE,
|
||||
...(this.blur ? BLUR_STYLE : {}),
|
||||
...(this.style ?? {}),
|
||||
};
|
||||
|
||||
this.visibleParts = this.buildParts(content, entities, null);
|
||||
this.rowsEllipsis = undefined;
|
||||
this.showTrailingEllipsis = false;
|
||||
this.showInlineReadMore = false;
|
||||
this.showBlockReadMore = false;
|
||||
this.pendingRowsOverflowCheck = false;
|
||||
|
||||
if (this.isCountConfig(ellipsisConfig)) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
const shouldTruncate = !this.mergedExpanded && count > 0 && content.length > count;
|
||||
|
||||
if (shouldTruncate) {
|
||||
const cutoff = this.computeEntitySafeCutoff(count, entities);
|
||||
this.visibleParts = this.buildParts(content, entities, cutoff);
|
||||
this.showTrailingEllipsis = true;
|
||||
this.showInlineReadMore = Boolean(ellipsisConfig.expandable);
|
||||
}
|
||||
} else if (this.isRowsConfig(ellipsisConfig) && !this.mergedExpanded) {
|
||||
this.rowsEllipsis = {
|
||||
rows: ellipsisConfig.rows,
|
||||
};
|
||||
|
||||
if (ellipsisConfig.expandable) {
|
||||
this.pendingRowsOverflowCheck = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.emitViewIfNeeded();
|
||||
this.scheduleRowsOverflowCheck();
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
private buildParts(
|
||||
content: string,
|
||||
entities: ReturnType<typeof findAllEntities>,
|
||||
upto: number | null,
|
||||
): ContentPart[] {
|
||||
const parts: ContentPart[] = [];
|
||||
let lastIndex = 0;
|
||||
|
||||
for (const [index, entity] of entities.entries()) {
|
||||
if (upto !== null && entity.start >= upto) {
|
||||
break;
|
||||
}
|
||||
|
||||
const textEnd = upto !== null ? Math.min(entity.start, upto) : entity.start;
|
||||
if (entity.start > lastIndex && lastIndex < textEnd) {
|
||||
parts.push({
|
||||
kind: "text",
|
||||
key: `text-${lastIndex}-${textEnd}`,
|
||||
text: content.slice(lastIndex, textEnd),
|
||||
});
|
||||
}
|
||||
|
||||
if (upto === null || entity.end <= upto) {
|
||||
if (entity.type === "mention") {
|
||||
parts.push({
|
||||
kind: "mention",
|
||||
key: `mention-${entity.start}-${index}`,
|
||||
entity,
|
||||
index,
|
||||
customRender: this.resolveCustomRender(this.renderMention, entity, index),
|
||||
});
|
||||
} else if (entity.type === "tag") {
|
||||
parts.push({
|
||||
kind: "tag",
|
||||
key: `tag-${entity.start}-${index}`,
|
||||
entity,
|
||||
index,
|
||||
customRender: this.resolveCustomRender(this.renderTag, entity, index),
|
||||
});
|
||||
} else {
|
||||
parts.push({
|
||||
kind: "link",
|
||||
key: `link-${entity.start}-${index}`,
|
||||
entity,
|
||||
index,
|
||||
customRender: this.resolveCustomRender(this.renderLink, entity, index),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
lastIndex = entity.end;
|
||||
}
|
||||
|
||||
if (upto === null) {
|
||||
if (lastIndex < content.length) {
|
||||
parts.push({
|
||||
kind: "text",
|
||||
key: `text-${lastIndex}-${content.length}`,
|
||||
text: content.slice(lastIndex),
|
||||
});
|
||||
}
|
||||
} else if (lastIndex < upto) {
|
||||
parts.push({
|
||||
kind: "text",
|
||||
key: `text-${lastIndex}-${upto}`,
|
||||
text: content.slice(lastIndex, upto),
|
||||
});
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
private resolveCustomRender<T>(
|
||||
rendererFn: ((entity: T, index: number) => AngularRenderResult) | undefined,
|
||||
entity: T,
|
||||
index: number,
|
||||
): AngularRenderResult | null {
|
||||
const rendered = rendererFn?.(entity, index);
|
||||
return rendered ?? null;
|
||||
}
|
||||
|
||||
private resolveEllipsisConfig(): AngularCountEllipsisConfig | AngularRowsEllipsisConfig | null {
|
||||
if (!this.ellipsis || typeof this.ellipsis !== "object") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.ellipsis;
|
||||
}
|
||||
|
||||
private isCountConfig(
|
||||
config: AngularCountEllipsisConfig | AngularRowsEllipsisConfig | null,
|
||||
): config is AngularCountEllipsisConfig {
|
||||
return Boolean(config && "count" in config);
|
||||
}
|
||||
|
||||
private isRowsConfig(
|
||||
config: AngularCountEllipsisConfig | AngularRowsEllipsisConfig | null,
|
||||
): config is AngularRowsEllipsisConfig {
|
||||
return Boolean(config && "rows" in config);
|
||||
}
|
||||
|
||||
private getMergedExpanded(
|
||||
config: AngularCountEllipsisConfig | AngularRowsEllipsisConfig | null,
|
||||
text: string,
|
||||
): boolean {
|
||||
const controlledExpanded =
|
||||
config && typeof config.expanded === "boolean"
|
||||
? config.expanded
|
||||
: undefined;
|
||||
|
||||
if (controlledExpanded !== undefined) {
|
||||
return controlledExpanded;
|
||||
}
|
||||
|
||||
if (this.isCountConfig(config)) {
|
||||
const count = config.count ?? 0;
|
||||
if (count <= 0 || text.length <= count) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return this.expanded;
|
||||
}
|
||||
|
||||
private resolveEllipsisSymbol(
|
||||
symbol: AngularCountEllipsisConfig["symbol"] | AngularRowsEllipsisConfig["symbol"],
|
||||
expanded: boolean,
|
||||
): string {
|
||||
if (typeof symbol === "function") {
|
||||
return symbol(expanded);
|
||||
}
|
||||
|
||||
return symbol ?? READ_MORE_TEXT;
|
||||
}
|
||||
|
||||
private notifyExpand(event: MouseEvent, expanded: boolean): void {
|
||||
const config = this.resolveEllipsisConfig();
|
||||
const onExpand = config?.onExpand;
|
||||
|
||||
if (onExpand) {
|
||||
if (onExpand.length >= 2) {
|
||||
(onExpand as (e: MouseEvent, info: { expanded: boolean }) => void)(event, { expanded });
|
||||
} else {
|
||||
(onExpand as (nextExpanded: boolean) => void)(expanded);
|
||||
}
|
||||
}
|
||||
|
||||
this.expandedChange.emit(expanded);
|
||||
}
|
||||
|
||||
private computeEntitySafeCutoff(
|
||||
count: number,
|
||||
entities: ReturnType<typeof findAllEntities>,
|
||||
): number {
|
||||
let cutoff = count;
|
||||
let extended = true;
|
||||
|
||||
while (extended) {
|
||||
extended = false;
|
||||
|
||||
for (const entity of entities) {
|
||||
if (entity.start < cutoff && entity.end > cutoff) {
|
||||
cutoff = entity.end;
|
||||
extended = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cutoff;
|
||||
}
|
||||
|
||||
private initObserver(): void {
|
||||
const host = this.containerRef?.nativeElement;
|
||||
if (!host) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof IntersectionObserver === "undefined") {
|
||||
this.isInsideView = true;
|
||||
this.emitViewIfNeeded();
|
||||
return;
|
||||
}
|
||||
|
||||
this.intersectionObserver = new IntersectionObserver(
|
||||
entries => {
|
||||
const entry = entries[0];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.isIntersecting && !this.isInsideView) {
|
||||
this.isInsideView = true;
|
||||
this.emitViewIfNeeded();
|
||||
}
|
||||
},
|
||||
{ threshold: 0.5 },
|
||||
);
|
||||
|
||||
this.intersectionObserver.observe(host);
|
||||
}
|
||||
|
||||
private emitViewIfNeeded(): void {
|
||||
if (!this.mergedExpanded || this.viewedFired || !this.isInsideView) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.viewedFired = true;
|
||||
this.onView?.();
|
||||
this.viewed.emit();
|
||||
}
|
||||
|
||||
private scheduleRowsOverflowCheck(): void {
|
||||
if (!this.pendingRowsOverflowCheck) {
|
||||
return;
|
||||
}
|
||||
|
||||
queueMicrotask(() => {
|
||||
this.refreshRowsOverflow();
|
||||
});
|
||||
}
|
||||
|
||||
private refreshRowsOverflow(): void {
|
||||
const paragraph = this.getParagraphElement();
|
||||
if (!paragraph) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasOverflow = paragraph.scrollHeight - paragraph.clientHeight > 1;
|
||||
if (this.showBlockReadMore !== hasOverflow) {
|
||||
this.showBlockReadMore = hasOverflow;
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
private getParagraphElement(): HTMLElement | null {
|
||||
const host = this.containerRef?.nativeElement;
|
||||
if (!host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return host.querySelector(`[${BODY_DATA_ATTR}="true"]`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import type {
|
||||
AngularContentEllipsisConfig,
|
||||
AngularLinkRenderer,
|
||||
AngularMentionRenderer,
|
||||
AngularRowsEllipsisConfig,
|
||||
AngularTagRenderer,
|
||||
} from "./types";
|
||||
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
|
||||
import { ContentTextWithSuggestionsComponent } from "./content-text-with-suggestions.component";
|
||||
|
||||
@Component({
|
||||
selector: "content-title-with-suggestions",
|
||||
standalone: true,
|
||||
imports: [ContentTextWithSuggestionsComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<content-text-with-suggestions
|
||||
[className]="className"
|
||||
weight="bold"
|
||||
[text]="text"
|
||||
[ellipsis]="normalizedEllipsis"
|
||||
[blur]="blur"
|
||||
[style]="style"
|
||||
[onView]="onView"
|
||||
[renderMention]="renderMention"
|
||||
[renderTag]="renderTag"
|
||||
[renderLink]="renderLink"
|
||||
(viewed)="viewed.emit()"
|
||||
(expandedChange)="expandedChange.emit($event)"
|
||||
/>
|
||||
`,
|
||||
})
|
||||
export class ContentTitleWithSuggestionsComponent {
|
||||
@Input() className: string | undefined;
|
||||
@Input() text: string | null | undefined;
|
||||
@Input() ellipsis: AngularContentEllipsisConfig | undefined;
|
||||
@Input() blur = false;
|
||||
@Input() style: Record<string, string | number> | null | undefined;
|
||||
@Input() onView: (() => void) | undefined;
|
||||
@Input() renderMention: AngularMentionRenderer | undefined;
|
||||
@Input() renderTag: AngularTagRenderer | undefined;
|
||||
@Input() renderLink: AngularLinkRenderer | undefined;
|
||||
|
||||
@Output() readonly viewed = new EventEmitter<void>();
|
||||
@Output() readonly expandedChange = new EventEmitter<boolean>();
|
||||
|
||||
get normalizedEllipsis(): AngularContentEllipsisConfig {
|
||||
if (this.ellipsis !== undefined) {
|
||||
return this.ellipsis;
|
||||
}
|
||||
|
||||
return { rows: 2 } satisfies AngularRowsEllipsisConfig;
|
||||
}
|
||||
}
|
||||
@@ -1,650 +1,57 @@
|
||||
import type { ContentEntity, LinkEntity, MentionEntity, TagEntity } from "../core";
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { findAllEntities } from "../core";
|
||||
import { ContentTextComponent } from "./content-text.component";
|
||||
import { ContentTextWithSuggestionsComponent } from "./content-text-with-suggestions.component";
|
||||
import { ContentTitleWithSuggestionsComponent } from "./content-title-with-suggestions.component";
|
||||
import { ContentSuggestionsRenderResultDirective } from "./render-result.directive";
|
||||
|
||||
export interface AngularTextToken {
|
||||
kind: "text";
|
||||
text: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
export interface AngularMentionToken {
|
||||
kind: "mention";
|
||||
entity: MentionEntity;
|
||||
}
|
||||
|
||||
export interface AngularTagToken {
|
||||
kind: "tag";
|
||||
entity: TagEntity;
|
||||
}
|
||||
|
||||
export interface AngularLinkToken {
|
||||
kind: "link";
|
||||
entity: LinkEntity;
|
||||
}
|
||||
|
||||
export type AngularContentToken =
|
||||
| AngularTextToken
|
||||
| AngularMentionToken
|
||||
| AngularTagToken
|
||||
| AngularLinkToken;
|
||||
|
||||
export interface AngularContentSnapshot {
|
||||
text: string;
|
||||
entities: ContentEntity[];
|
||||
tokens: AngularContentToken[];
|
||||
}
|
||||
|
||||
export type AngularEllipsisSymbol = string | ((expanded: boolean) => string);
|
||||
|
||||
export type AngularCountEllipsisConfig = {
|
||||
count: number;
|
||||
rows?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: (expanded: boolean) => void;
|
||||
};
|
||||
|
||||
export type AngularRowsEllipsisConfig = {
|
||||
rows: number;
|
||||
count?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: (expanded: boolean) => void;
|
||||
};
|
||||
|
||||
export type AngularContentEllipsisConfig =
|
||||
| AngularCountEllipsisConfig
|
||||
| AngularRowsEllipsisConfig
|
||||
| false;
|
||||
|
||||
export type AngularRenderResult = Node | string | number | null | undefined;
|
||||
|
||||
export type AngularMentionRenderer = (
|
||||
entity: MentionEntity,
|
||||
index: number,
|
||||
) => AngularRenderResult;
|
||||
|
||||
export type AngularTagRenderer = (
|
||||
entity: TagEntity,
|
||||
index: number,
|
||||
) => AngularRenderResult;
|
||||
|
||||
export type AngularLinkRenderer = (
|
||||
entity: LinkEntity,
|
||||
index: number,
|
||||
) => AngularRenderResult;
|
||||
|
||||
export interface AngularContentTextProps {
|
||||
className?: string;
|
||||
weight?: "normal" | "bold";
|
||||
text?: string | null;
|
||||
ellipsis?: AngularContentEllipsisConfig;
|
||||
blur?: boolean;
|
||||
style?: Record<string, string | number> | null;
|
||||
onView?: () => void;
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
renderLink?: AngularLinkRenderer;
|
||||
}
|
||||
|
||||
export type AngularContentTextWithSuggestionsProps = Omit<
|
||||
export type {
|
||||
AngularContentEllipsisConfig,
|
||||
AngularContentSnapshot,
|
||||
AngularContentTextProps,
|
||||
"renderMention" | "renderTag"
|
||||
> & {
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
};
|
||||
|
||||
export type AngularContentTitleWithSuggestionsProps = Omit<
|
||||
AngularContentTextWithSuggestionsProps,
|
||||
"weight"
|
||||
>;
|
||||
AngularContentTitleWithSuggestionsProps,
|
||||
AngularContentToken,
|
||||
AngularCountEllipsisConfig,
|
||||
AngularEllipsisExpandHandler,
|
||||
AngularEllipsisSymbol,
|
||||
AngularExpandInfo,
|
||||
AngularLinkRenderer,
|
||||
AngularLinkToken,
|
||||
AngularMentionRenderer,
|
||||
AngularMentionToken,
|
||||
AngularRenderResult,
|
||||
AngularRowsEllipsisConfig,
|
||||
AngularTagRenderer,
|
||||
AngularTagToken,
|
||||
AngularTextToken,
|
||||
} from "./types";
|
||||
|
||||
export interface AngularContentTextRendererState {
|
||||
props: AngularContentTextProps;
|
||||
snapshot: AngularContentSnapshot;
|
||||
expanded: boolean;
|
||||
}
|
||||
export {
|
||||
AngularContentSuggestionsAdapter,
|
||||
buildAngularTagHref,
|
||||
createAngularContentTokens,
|
||||
} from "./tokens";
|
||||
|
||||
const LINK_COLOR = "#1677ff";
|
||||
const READ_MORE_TEXT = "Читать полностью";
|
||||
|
||||
const toKebabCase = (value: string) => value.replace(/[A-Z]/g, char => `-${char.toLowerCase()}`);
|
||||
|
||||
const applyStyleObject = (
|
||||
element: HTMLElement,
|
||||
styles?: Record<string, string | number> | null,
|
||||
) => {
|
||||
if (!styles) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [key, rawValue] of Object.entries(styles)) {
|
||||
if (rawValue === null || rawValue === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const styleValue =
|
||||
typeof rawValue === "number" ? `${rawValue}px` : String(rawValue);
|
||||
|
||||
if (key.startsWith("--")) {
|
||||
element.style.setProperty(key, styleValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
const cssKey = key.includes("-") ? key : toKebabCase(key);
|
||||
element.style.setProperty(cssKey, styleValue);
|
||||
}
|
||||
export {
|
||||
ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
};
|
||||
|
||||
const toNode = (value: AngularRenderResult): Node | null => {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value instanceof Node) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return document.createTextNode(String(value));
|
||||
};
|
||||
|
||||
const resolveEllipsisSymbol = (
|
||||
symbol: AngularEllipsisSymbol | undefined,
|
||||
expanded: boolean,
|
||||
): string => {
|
||||
if (typeof symbol === "function") {
|
||||
return symbol(expanded);
|
||||
}
|
||||
|
||||
return symbol ?? READ_MORE_TEXT;
|
||||
};
|
||||
|
||||
const buildAngularTagHref = (entity: TagEntity): string => {
|
||||
return `/search/?query=${encodeURIComponent(entity.tag.toLowerCase())}`;
|
||||
};
|
||||
|
||||
export { buildAngularTagHref };
|
||||
|
||||
export const createAngularContentTokens = (
|
||||
inputText: string | null | undefined,
|
||||
): AngularContentToken[] => {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
|
||||
let cursor = 0;
|
||||
const tokens: AngularContentToken[] = [];
|
||||
|
||||
for (const entity of entities) {
|
||||
if (entity.start > cursor) {
|
||||
tokens.push({
|
||||
kind: "text",
|
||||
text: text.slice(cursor, entity.start),
|
||||
start: cursor,
|
||||
end: entity.start,
|
||||
});
|
||||
}
|
||||
|
||||
if (entity.type === "mention") {
|
||||
tokens.push({
|
||||
kind: "mention",
|
||||
entity,
|
||||
});
|
||||
} else if (entity.type === "tag") {
|
||||
tokens.push({
|
||||
kind: "tag",
|
||||
entity,
|
||||
});
|
||||
} else {
|
||||
tokens.push({
|
||||
kind: "link",
|
||||
entity,
|
||||
});
|
||||
}
|
||||
|
||||
cursor = entity.end;
|
||||
}
|
||||
|
||||
if (cursor < text.length) {
|
||||
tokens.push({
|
||||
kind: "text",
|
||||
text: text.slice(cursor),
|
||||
start: cursor,
|
||||
end: text.length,
|
||||
});
|
||||
}
|
||||
|
||||
return tokens;
|
||||
};
|
||||
|
||||
export class AngularContentSuggestionsAdapter {
|
||||
snapshot(inputText: string | null | undefined): AngularContentSnapshot {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
const tokens = createAngularContentTokens(text);
|
||||
|
||||
return {
|
||||
text,
|
||||
entities,
|
||||
tokens,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class AngularContentTextRenderer {
|
||||
private host: HTMLElement | null = null;
|
||||
private props: AngularContentTextProps = {};
|
||||
private expanded = false;
|
||||
private viewed = false;
|
||||
private hostInsideView = false;
|
||||
private observer: IntersectionObserver | null = null;
|
||||
|
||||
attach(host: HTMLElement, props: AngularContentTextProps = {}): AngularContentTextRendererState {
|
||||
this.destroy();
|
||||
|
||||
this.host = host;
|
||||
this.props = { ...props };
|
||||
this.expanded = false;
|
||||
this.viewed = false;
|
||||
this.hostInsideView = false;
|
||||
|
||||
this.initObserver();
|
||||
this.render();
|
||||
|
||||
return this.getState();
|
||||
}
|
||||
|
||||
update(nextProps: AngularContentTextProps): AngularContentTextRendererState {
|
||||
this.props = {
|
||||
...this.props,
|
||||
...nextProps,
|
||||
};
|
||||
|
||||
this.render();
|
||||
|
||||
return this.getState();
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
if (this.observer && this.host) {
|
||||
this.observer.unobserve(this.host);
|
||||
this.observer.disconnect();
|
||||
}
|
||||
|
||||
this.observer = null;
|
||||
|
||||
if (this.host) {
|
||||
this.host.innerHTML = "";
|
||||
}
|
||||
|
||||
this.host = null;
|
||||
this.props = {};
|
||||
this.expanded = false;
|
||||
this.viewed = false;
|
||||
this.hostInsideView = false;
|
||||
}
|
||||
|
||||
getState(): AngularContentTextRendererState {
|
||||
return {
|
||||
props: { ...this.props },
|
||||
snapshot: this.createSnapshot(),
|
||||
expanded: this.getMergedExpanded(this.resolveEllipsisConfig(), this.getText()),
|
||||
};
|
||||
}
|
||||
|
||||
private getText(): string {
|
||||
return this.props.text ?? "";
|
||||
}
|
||||
|
||||
private createSnapshot(): AngularContentSnapshot {
|
||||
const text = this.getText();
|
||||
const entities = findAllEntities(text);
|
||||
const tokens = createAngularContentTokens(text);
|
||||
|
||||
return {
|
||||
text,
|
||||
entities,
|
||||
tokens,
|
||||
};
|
||||
}
|
||||
|
||||
private resolveEllipsisConfig():
|
||||
| AngularCountEllipsisConfig
|
||||
| AngularRowsEllipsisConfig
|
||||
| null {
|
||||
const ellipsis = this.props.ellipsis;
|
||||
|
||||
if (!ellipsis || typeof ellipsis !== "object") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ellipsis;
|
||||
}
|
||||
|
||||
private getMergedExpanded(
|
||||
ellipsisConfig: AngularCountEllipsisConfig | AngularRowsEllipsisConfig | null,
|
||||
text: string,
|
||||
): boolean {
|
||||
const controlledExpanded =
|
||||
ellipsisConfig && typeof ellipsisConfig.expanded === "boolean"
|
||||
? ellipsisConfig.expanded
|
||||
: undefined;
|
||||
|
||||
if (controlledExpanded !== undefined) {
|
||||
return controlledExpanded;
|
||||
}
|
||||
|
||||
if (ellipsisConfig && "count" in ellipsisConfig) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
if (count <= 0 || text.length <= count) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return this.expanded;
|
||||
}
|
||||
|
||||
private createDefaultMentionNode(entity: MentionEntity): Node {
|
||||
const span = document.createElement("span");
|
||||
span.style.color = LINK_COLOR;
|
||||
span.style.fontWeight = this.props.weight === "bold" ? "700" : "400";
|
||||
span.textContent = entity.displayText;
|
||||
return span;
|
||||
}
|
||||
|
||||
private createDefaultTagNode(entity: TagEntity): Node {
|
||||
const span = document.createElement("span");
|
||||
span.style.color = LINK_COLOR;
|
||||
span.style.fontWeight = this.props.weight === "bold" ? "700" : "400";
|
||||
span.textContent = entity.text;
|
||||
return span;
|
||||
}
|
||||
|
||||
private createDefaultLinkNode(entity: LinkEntity): Node {
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = entity.url;
|
||||
anchor.target = "_blank";
|
||||
anchor.referrerPolicy = "no-referrer";
|
||||
anchor.style.color = LINK_COLOR;
|
||||
anchor.style.fontWeight = this.props.weight === "bold" ? "700" : "400";
|
||||
anchor.textContent = entity.text;
|
||||
return anchor;
|
||||
}
|
||||
|
||||
private renderEntity(entity: ContentEntity, index: number): Node | null {
|
||||
if (entity.type === "mention") {
|
||||
const customNode = this.props.renderMention?.(entity, index);
|
||||
return toNode(customNode) ?? this.createDefaultMentionNode(entity);
|
||||
}
|
||||
|
||||
if (entity.type === "tag") {
|
||||
const customNode = this.props.renderTag?.(entity, index);
|
||||
return toNode(customNode) ?? this.createDefaultTagNode(entity);
|
||||
}
|
||||
|
||||
const customNode = this.props.renderLink?.(entity, index);
|
||||
return toNode(customNode) ?? this.createDefaultLinkNode(entity);
|
||||
}
|
||||
|
||||
private buildTextNodes(
|
||||
text: string,
|
||||
entities: ContentEntity[],
|
||||
upto: number | null = null,
|
||||
): Node[] {
|
||||
let lastIndex = 0;
|
||||
const nodes: Node[] = [];
|
||||
|
||||
for (const [index, entity] of entities.entries()) {
|
||||
if (upto !== null && entity.start >= upto) {
|
||||
break;
|
||||
}
|
||||
|
||||
const textEnd = upto !== null ? Math.min(entity.start, upto) : entity.start;
|
||||
if (entity.start > lastIndex && lastIndex < textEnd) {
|
||||
nodes.push(document.createTextNode(text.slice(lastIndex, textEnd)));
|
||||
}
|
||||
|
||||
if (upto === null || entity.end <= upto) {
|
||||
const entityNode = this.renderEntity(entity, index);
|
||||
if (entityNode) {
|
||||
nodes.push(entityNode);
|
||||
}
|
||||
}
|
||||
|
||||
lastIndex = entity.end;
|
||||
}
|
||||
|
||||
if (upto === null) {
|
||||
if (lastIndex < text.length) {
|
||||
nodes.push(document.createTextNode(text.slice(lastIndex)));
|
||||
}
|
||||
} else if (lastIndex < upto) {
|
||||
nodes.push(document.createTextNode(text.slice(lastIndex, upto)));
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private applyBaseParagraphStyle(element: HTMLElement): void {
|
||||
element.style.whiteSpace = "pre-wrap";
|
||||
element.style.fontWeight = this.props.weight === "bold" ? "700" : "400";
|
||||
element.style.setProperty("-webkit-touch-callout", "default");
|
||||
element.style.setProperty("-webkit-user-select", "text");
|
||||
element.style.setProperty("-khtml-user-select", "text");
|
||||
element.style.setProperty("-moz-user-select", "text");
|
||||
element.style.setProperty("-ms-user-select", "text");
|
||||
element.style.setProperty("user-select", "text");
|
||||
|
||||
if (this.props.blur) {
|
||||
element.style.filter = "blur(3px)";
|
||||
element.style.setProperty("-webkit-user-select", "none");
|
||||
element.style.setProperty("-khtml-user-select", "none");
|
||||
element.style.setProperty("-moz-user-select", "none");
|
||||
element.style.setProperty("-ms-user-select", "none");
|
||||
element.style.setProperty("user-select", "none");
|
||||
element.style.pointerEvents = "none";
|
||||
}
|
||||
|
||||
applyStyleObject(element, this.props.style);
|
||||
}
|
||||
|
||||
private buildReadMoreButton(symbol: AngularEllipsisSymbol | undefined): HTMLButtonElement {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.textContent = resolveEllipsisSymbol(symbol, this.expanded);
|
||||
button.style.border = "0";
|
||||
button.style.background = "none";
|
||||
button.style.padding = "0";
|
||||
button.style.marginLeft = "6px";
|
||||
button.style.cursor = "pointer";
|
||||
button.style.color = LINK_COLOR;
|
||||
button.style.fontWeight = "700";
|
||||
button.onclick = event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.handleExpand();
|
||||
};
|
||||
return button;
|
||||
}
|
||||
|
||||
private handleExpand(): void {
|
||||
const ellipsisConfig = this.resolveEllipsisConfig();
|
||||
|
||||
if (!ellipsisConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.expanded = true;
|
||||
ellipsisConfig.onExpand?.(true);
|
||||
this.render();
|
||||
}
|
||||
|
||||
private computeEntitySafeCutoff(
|
||||
count: number,
|
||||
entities: ContentEntity[],
|
||||
): number {
|
||||
let cutoff = count;
|
||||
let extended = true;
|
||||
|
||||
while (extended) {
|
||||
extended = false;
|
||||
|
||||
for (const entity of entities) {
|
||||
if (entity.start < cutoff && entity.end > cutoff) {
|
||||
cutoff = entity.end;
|
||||
extended = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cutoff;
|
||||
}
|
||||
|
||||
private hasVerticalOverflow(element: HTMLElement): boolean {
|
||||
return element.scrollHeight - element.clientHeight > 1;
|
||||
}
|
||||
|
||||
private render(): void {
|
||||
if (!this.host) {
|
||||
return;
|
||||
}
|
||||
|
||||
const text = this.getText();
|
||||
const entities = findAllEntities(text);
|
||||
const ellipsisConfig = this.resolveEllipsisConfig();
|
||||
const mergedExpanded = this.getMergedExpanded(ellipsisConfig, text);
|
||||
|
||||
const wrapper = document.createElement("div");
|
||||
const paragraph = document.createElement("div");
|
||||
|
||||
if (this.props.className) {
|
||||
paragraph.className = this.props.className;
|
||||
}
|
||||
|
||||
this.applyBaseParagraphStyle(paragraph);
|
||||
|
||||
if (ellipsisConfig && "count" in ellipsisConfig) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
const shouldTruncate = !mergedExpanded && count > 0 && text.length > count;
|
||||
|
||||
if (shouldTruncate) {
|
||||
const cutoff = this.computeEntitySafeCutoff(count, entities);
|
||||
const nodes = this.buildTextNodes(text, entities, cutoff);
|
||||
|
||||
paragraph.append(...nodes);
|
||||
paragraph.append(document.createTextNode("…"));
|
||||
|
||||
if (ellipsisConfig.expandable) {
|
||||
paragraph.append(this.buildReadMoreButton(ellipsisConfig.symbol));
|
||||
}
|
||||
} else {
|
||||
paragraph.append(...this.buildTextNodes(text, entities));
|
||||
}
|
||||
} else {
|
||||
paragraph.append(...this.buildTextNodes(text, entities));
|
||||
|
||||
if (ellipsisConfig && "rows" in ellipsisConfig && !mergedExpanded) {
|
||||
paragraph.style.display = "-webkit-box";
|
||||
paragraph.style.setProperty("-webkit-box-orient", "vertical");
|
||||
paragraph.style.setProperty("-webkit-line-clamp", String(ellipsisConfig.rows));
|
||||
paragraph.style.overflow = "hidden";
|
||||
|
||||
// Важно: сначала рендерим paragraph в DOM, потом меряем реальный overflow.
|
||||
wrapper.append(paragraph);
|
||||
this.host.replaceChildren(wrapper);
|
||||
|
||||
if (ellipsisConfig.expandable && this.hasVerticalOverflow(paragraph)) {
|
||||
wrapper.append(this.buildReadMoreButton(ellipsisConfig.symbol));
|
||||
}
|
||||
|
||||
this.emitOnViewIfNeeded(mergedExpanded);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
wrapper.append(paragraph);
|
||||
this.host.replaceChildren(wrapper);
|
||||
this.emitOnViewIfNeeded(mergedExpanded);
|
||||
}
|
||||
|
||||
private initObserver(): void {
|
||||
if (!this.host) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof IntersectionObserver === "undefined") {
|
||||
this.hostInsideView = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.observer = new IntersectionObserver(
|
||||
entries => {
|
||||
const entry = entries[0];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.isIntersecting && !this.hostInsideView) {
|
||||
this.hostInsideView = true;
|
||||
const ellipsisConfig = this.resolveEllipsisConfig();
|
||||
const mergedExpanded = this.getMergedExpanded(ellipsisConfig, this.getText());
|
||||
this.emitOnViewIfNeeded(mergedExpanded);
|
||||
}
|
||||
},
|
||||
{ threshold: 0.5 },
|
||||
);
|
||||
|
||||
this.observer.observe(this.host);
|
||||
}
|
||||
|
||||
private emitOnViewIfNeeded(mergedExpanded: boolean): void {
|
||||
if (!mergedExpanded || this.viewed || !this.hostInsideView) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.viewed = true;
|
||||
this.props.onView?.();
|
||||
}
|
||||
}
|
||||
|
||||
export class AngularContentTextWithSuggestionsRenderer extends AngularContentTextRenderer {}
|
||||
|
||||
export class AngularContentTitleWithSuggestionsRenderer {
|
||||
private readonly renderer = new AngularContentTextWithSuggestionsRenderer();
|
||||
|
||||
attach(
|
||||
host: HTMLElement,
|
||||
props: AngularContentTitleWithSuggestionsProps = {},
|
||||
): AngularContentTextRendererState {
|
||||
return this.renderer.attach(host, this.normalizeProps(props));
|
||||
}
|
||||
|
||||
update(props: AngularContentTitleWithSuggestionsProps): AngularContentTextRendererState {
|
||||
return this.renderer.update(this.normalizeProps(props));
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.renderer.destroy();
|
||||
}
|
||||
|
||||
getState(): AngularContentTextRendererState {
|
||||
return this.renderer.getState();
|
||||
}
|
||||
|
||||
private normalizeProps(
|
||||
props: AngularContentTitleWithSuggestionsProps,
|
||||
): AngularContentTextWithSuggestionsProps {
|
||||
return {
|
||||
...props,
|
||||
weight: "bold",
|
||||
blur: props.blur ?? false,
|
||||
ellipsis: props.ellipsis === undefined ? { rows: 2 } : props.ellipsis,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export { toKebabCase };
|
||||
@NgModule({
|
||||
imports: [
|
||||
ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
],
|
||||
exports: [
|
||||
ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
],
|
||||
})
|
||||
export class ContentSuggestionsAngularModule {}
|
||||
|
||||
1
packages/content-suggestions/src/angular/public-api.ts
Normal file
1
packages/content-suggestions/src/angular/public-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./index";
|
||||
@@ -0,0 +1,43 @@
|
||||
import type { AngularRenderResult } from "./types";
|
||||
|
||||
import { Directive, ElementRef, Input, OnChanges, Renderer2 } from "@angular/core";
|
||||
|
||||
@Directive({
|
||||
selector: "[contentSuggestionsRenderResult]",
|
||||
standalone: true,
|
||||
})
|
||||
export class ContentSuggestionsRenderResultDirective implements OnChanges {
|
||||
@Input() contentSuggestionsRenderResult: AngularRenderResult;
|
||||
|
||||
constructor(
|
||||
private readonly elementRef: ElementRef<HTMLElement>,
|
||||
private readonly renderer: Renderer2,
|
||||
) {
|
||||
this.renderer.setStyle(this.elementRef.nativeElement, "display", "contents");
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.clearHost();
|
||||
|
||||
const value = this.contentSuggestionsRenderResult;
|
||||
if (value === null || value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof Node) {
|
||||
this.renderer.appendChild(this.elementRef.nativeElement, value);
|
||||
return;
|
||||
}
|
||||
|
||||
const textNode = this.renderer.createText(String(value));
|
||||
this.renderer.appendChild(this.elementRef.nativeElement, textNode);
|
||||
}
|
||||
|
||||
private clearHost(): void {
|
||||
const host = this.elementRef.nativeElement;
|
||||
|
||||
while (host.firstChild) {
|
||||
host.removeChild(host.firstChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
83
packages/content-suggestions/src/angular/tokens.ts
Normal file
83
packages/content-suggestions/src/angular/tokens.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import type {
|
||||
AngularContentSnapshot,
|
||||
AngularContentToken,
|
||||
AngularLinkToken,
|
||||
AngularMentionToken,
|
||||
AngularTagToken,
|
||||
AngularTextToken,
|
||||
} from "./types";
|
||||
|
||||
import type { ContentEntity, TagEntity } from "../core";
|
||||
|
||||
import { findAllEntities } from "../core";
|
||||
|
||||
const mapEntityToToken = (entity: ContentEntity): AngularMentionToken | AngularTagToken | AngularLinkToken => {
|
||||
if (entity.type === "mention") {
|
||||
return {
|
||||
kind: "mention",
|
||||
entity,
|
||||
};
|
||||
}
|
||||
|
||||
if (entity.type === "tag") {
|
||||
return {
|
||||
kind: "tag",
|
||||
entity,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
kind: "link",
|
||||
entity,
|
||||
};
|
||||
};
|
||||
|
||||
const createTextToken = (text: string, start: number, end: number): AngularTextToken => ({
|
||||
kind: "text",
|
||||
text,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
|
||||
export const createAngularContentTokens = (
|
||||
inputText: string | null | undefined,
|
||||
): AngularContentToken[] => {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
|
||||
let cursor = 0;
|
||||
const tokens: AngularContentToken[] = [];
|
||||
|
||||
for (const entity of entities) {
|
||||
if (entity.start > cursor) {
|
||||
tokens.push(createTextToken(text.slice(cursor, entity.start), cursor, entity.start));
|
||||
}
|
||||
|
||||
tokens.push(mapEntityToToken(entity));
|
||||
cursor = entity.end;
|
||||
}
|
||||
|
||||
if (cursor < text.length) {
|
||||
tokens.push(createTextToken(text.slice(cursor), cursor, text.length));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
};
|
||||
|
||||
export const buildAngularTagHref = (entity: TagEntity): string => {
|
||||
return `/search/?query=${encodeURIComponent(entity.tag.toLowerCase())}`;
|
||||
};
|
||||
|
||||
export class AngularContentSuggestionsAdapter {
|
||||
snapshot(inputText: string | null | undefined): AngularContentSnapshot {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
const tokens = createAngularContentTokens(text);
|
||||
|
||||
return {
|
||||
text,
|
||||
entities,
|
||||
tokens,
|
||||
};
|
||||
}
|
||||
}
|
||||
113
packages/content-suggestions/src/angular/types.ts
Normal file
113
packages/content-suggestions/src/angular/types.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import type { ContentEntity, LinkEntity, MentionEntity, TagEntity } from "../core";
|
||||
|
||||
import type { TypographyWeight } from "@hublib-web/tach-typography/core";
|
||||
|
||||
export interface AngularTextToken {
|
||||
kind: "text";
|
||||
text: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
export interface AngularMentionToken {
|
||||
kind: "mention";
|
||||
entity: MentionEntity;
|
||||
}
|
||||
|
||||
export interface AngularTagToken {
|
||||
kind: "tag";
|
||||
entity: TagEntity;
|
||||
}
|
||||
|
||||
export interface AngularLinkToken {
|
||||
kind: "link";
|
||||
entity: LinkEntity;
|
||||
}
|
||||
|
||||
export type AngularContentToken =
|
||||
| AngularTextToken
|
||||
| AngularMentionToken
|
||||
| AngularTagToken
|
||||
| AngularLinkToken;
|
||||
|
||||
export interface AngularContentSnapshot {
|
||||
text: string;
|
||||
entities: ContentEntity[];
|
||||
tokens: AngularContentToken[];
|
||||
}
|
||||
|
||||
export interface AngularExpandInfo {
|
||||
expanded: boolean;
|
||||
}
|
||||
|
||||
export type AngularEllipsisSymbol = string | ((expanded: boolean) => string);
|
||||
|
||||
export type AngularEllipsisExpandHandler =
|
||||
| ((expanded: boolean) => void)
|
||||
| ((event: MouseEvent, info: AngularExpandInfo) => void);
|
||||
|
||||
export type AngularCountEllipsisConfig = {
|
||||
count: number;
|
||||
rows?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: AngularEllipsisExpandHandler;
|
||||
};
|
||||
|
||||
export type AngularRowsEllipsisConfig = {
|
||||
rows: number;
|
||||
count?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: AngularEllipsisExpandHandler;
|
||||
};
|
||||
|
||||
export type AngularContentEllipsisConfig =
|
||||
| AngularCountEllipsisConfig
|
||||
| AngularRowsEllipsisConfig
|
||||
| false;
|
||||
|
||||
export type AngularRenderResult = Node | string | number | null | undefined;
|
||||
|
||||
export type AngularMentionRenderer = (
|
||||
entity: MentionEntity,
|
||||
index: number,
|
||||
) => AngularRenderResult;
|
||||
|
||||
export type AngularTagRenderer = (
|
||||
entity: TagEntity,
|
||||
index: number,
|
||||
) => AngularRenderResult;
|
||||
|
||||
export type AngularLinkRenderer = (
|
||||
entity: LinkEntity,
|
||||
index: number,
|
||||
) => AngularRenderResult;
|
||||
|
||||
export interface AngularContentTextProps {
|
||||
className?: string;
|
||||
weight?: TypographyWeight;
|
||||
text?: string | null;
|
||||
ellipsis?: AngularContentEllipsisConfig;
|
||||
blur?: boolean;
|
||||
style?: Record<string, string | number> | null;
|
||||
onView?: () => void;
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
renderLink?: AngularLinkRenderer;
|
||||
}
|
||||
|
||||
export type AngularContentTextWithSuggestionsProps = Omit<
|
||||
AngularContentTextProps,
|
||||
"renderMention" | "renderTag"
|
||||
> & {
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
};
|
||||
|
||||
export type AngularContentTitleWithSuggestionsProps = Omit<
|
||||
AngularContentTextWithSuggestionsProps,
|
||||
"weight"
|
||||
>;
|
||||
@@ -4,8 +4,10 @@ import React from "react";
|
||||
|
||||
import { ContentTextWithSuggestions } from "./content-text-with-suggestions";
|
||||
|
||||
interface ContentTitleWithSuggestionsProps
|
||||
extends Omit<ComponentProps<typeof ContentTextWithSuggestions>, "weight"> {}
|
||||
type ContentTitleWithSuggestionsProps = Omit<
|
||||
ComponentProps<typeof ContentTextWithSuggestions>,
|
||||
"weight"
|
||||
>;
|
||||
|
||||
export const ContentTitleWithSuggestions = ({
|
||||
text,
|
||||
|
||||
20
packages/content-suggestions/tsconfig.angular.json
Normal file
20
packages/content-suggestions/tsconfig.angular.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist/angular",
|
||||
"types": ["node"],
|
||||
"module": "ES2022",
|
||||
"target": "ES2022",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"importHelpers": true
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"compilationMode": "partial"
|
||||
},
|
||||
"files": ["src/angular/public-api.ts"],
|
||||
"include": ["src/angular/**/*.ts", "src/core/**/*.ts"],
|
||||
"exclude": ["dist", "src/**/*.test.ts", "src/react", "src/stories"]
|
||||
}
|
||||
@@ -4,7 +4,6 @@ export default defineConfig({
|
||||
entry: {
|
||||
"core/index": "src/core/index.ts",
|
||||
"react/index": "src/react/index.tsx",
|
||||
"angular/index": "src/angular/index.ts",
|
||||
},
|
||||
format: ["esm", "cjs"],
|
||||
dts: true,
|
||||
@@ -21,5 +20,6 @@ export default defineConfig({
|
||||
"@angular/core",
|
||||
"@angular/common",
|
||||
"@hublib-web/tach-typography/react",
|
||||
"@hublib-web/tach-typography/angular",
|
||||
],
|
||||
});
|
||||
|
||||
@@ -58,6 +58,14 @@ export const Example = () => (
|
||||
);
|
||||
```
|
||||
|
||||
Markdown as a prop (keeps existing API):
|
||||
|
||||
```tsx
|
||||
<TachTypography.Text.AccentH1 markdownEnabled>
|
||||
**Bold** _italic_ [Docs](https://example.com)
|
||||
</TachTypography.Text.AccentH1>
|
||||
```
|
||||
|
||||
## Angular usage (NG-ZORRO)
|
||||
|
||||
```ts
|
||||
@@ -92,6 +100,12 @@ import { TachTypographyComponent } from "@hublib-web/tach-typography/angular";
|
||||
export class ExampleComponent {}
|
||||
```
|
||||
|
||||
Markdown in Angular:
|
||||
|
||||
```html
|
||||
<tach-typography variant="Body" [markdownEnabled]="true" [markdown]="'**Bold** _italic_'"></tach-typography>
|
||||
```
|
||||
|
||||
## Storybook (dev/design system)
|
||||
|
||||
Run from repository root:
|
||||
|
||||
@@ -56,21 +56,25 @@ export declare class TachTypographyHostPropsDirective implements OnChanges {
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyHostPropsDirective, never>;
|
||||
static ɵdir: i0.ɵɵDirectiveDeclaration<TachTypographyHostPropsDirective, "[tachTypographyHostProps]", never, { "tachTypographyHostProps": { "alias": "tachTypographyHostProps"; "required": false; }; }, {}, never, never, true, never>;
|
||||
}
|
||||
export declare class TachTypographyComponent {
|
||||
export declare class TachTypographyComponent implements OnChanges {
|
||||
hostTag: TachTypographyHostTag;
|
||||
variant: TypographyVariant;
|
||||
color: TypographyColor;
|
||||
weight: TypographyWeight;
|
||||
clickable: boolean;
|
||||
markdownEnabled: boolean;
|
||||
markdown: string | undefined;
|
||||
className: string | undefined;
|
||||
ellipsis: EllipsisOptions | undefined;
|
||||
nzProps: TachTypographyNzProps | undefined;
|
||||
hostProps: TachTypographyHostProps | undefined;
|
||||
preserveStyle: Record<string, string | number> | undefined;
|
||||
readonly tachClick: EventEmitter<MouseEvent>;
|
||||
renderedMarkdown: string;
|
||||
ngOnChanges(_changes: SimpleChanges): void;
|
||||
handleClick(event: MouseEvent): void;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyComponent, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<TachTypographyComponent, "tach-typography", never, { "hostTag": { "alias": "as"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "color": { "alias": "color"; "required": false; }; "weight": { "alias": "weight"; "required": false; }; "clickable": { "alias": "clickable"; "required": false; }; "className": { "alias": "className"; "required": false; }; "ellipsis": { "alias": "ellipsis"; "required": false; }; "nzProps": { "alias": "nzProps"; "required": false; }; "hostProps": { "alias": "hostProps"; "required": false; }; "preserveStyle": { "alias": "preserveStyle"; "required": false; }; }, { "tachClick": "tachClick"; }, never, ["*", "*", "*", "*", "*", "*", "*"], true, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<TachTypographyComponent, "tach-typography", never, { "hostTag": { "alias": "as"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "color": { "alias": "color"; "required": false; }; "weight": { "alias": "weight"; "required": false; }; "clickable": { "alias": "clickable"; "required": false; }; "markdownEnabled": { "alias": "markdownEnabled"; "required": false; }; "markdown": { "alias": "markdown"; "required": false; }; "className": { "alias": "className"; "required": false; }; "ellipsis": { "alias": "ellipsis"; "required": false; }; "nzProps": { "alias": "nzProps"; "required": false; }; "hostProps": { "alias": "hostProps"; "required": false; }; "preserveStyle": { "alias": "preserveStyle"; "required": false; }; }, { "tachClick": "tachClick"; }, never, ["*", "*", "*", "*", "*", "*", "*"], true, never>;
|
||||
}
|
||||
export declare class TachTypographyNzModule {
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyNzModule, never>;
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/angular/index.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/angular/index.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,UAAU,EACV,YAAY,EAIZ,SAAS,EAET,SAAS,EACT,aAAa,EACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAsB,MAAM,0BAA0B,CAAC;AAErF,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACtB,MAAM,SAAS,CAAC;;;AAEjB,MAAM,MAAM,2BAA2B,GAAG,sBAAsB,CAAC;AAEjE,MAAM,WAAW,8BAA+B,SAAQ,uBAAuB;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;CACjD;AAED,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACnF,KAAK,yBAAyB,CAAC,CAAC,IAAI;KACjC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,GACxD,KAAK,GACL,CAAC,CAAC,CAAC,CAAC,SAAS,YAAY,CAAC,OAAO,CAAC,GAChC,KAAK,GACL,CAAC;CACR,CAAC,MAAM,CAAC,CAAC,CAAC;AACX,KAAK,oBAAoB,GAAG,OAAO,CAAC,yBAAyB,CAAC,qBAAqB,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC,CAAC;AACrG,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,oBAAoB,CAAC,CAAC,CAAC;AAC/F,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAa9D,eAAO,MAAM,8BAA8B,GACzC,UAAS,2BAAgC,KACxC,MAEF,CAAC;AAEF,eAAO,MAAM,8BAA8B,GACzC,UAAS,2BAAgC,KACxC,MAAM,EAER,CAAC;AAEF,eAAO,MAAM,2BAA2B,GACtC,WAAW,eAAe,EAC1B,gBAAe,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAM,KAClD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAWhC,CAAC;AAEF,qBAIa,uBAAwB,YAAW,SAAS;IAarD,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAblB,cAAc,EAAE,iBAAiB,GAAG,EAAE,GAAG,SAAS,CAAC;IACnD,qBAAqB,EAAE,iBAAiB,CAAU;IAClD,mBAAmB,EAAE,eAAe,CAAa;IACjD,oBAAoB,EAAE,gBAAgB,CAAY;IAClD,uBAAuB,UAAS;IAChC,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,sBAAsB,EAAE,eAAe,GAAG,SAAS,CAAC;IAE7D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAqB;gBAGzC,UAAU,EAAE,UAAU,CAAC,WAAW,CAAC,EACnC,QAAQ,EAAE,SAAS;IAGtC,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAK1C,OAAO,CAAC,WAAW;IA2BnB,OAAO,CAAC,kBAAkB;yCAjDf,uBAAuB;2CAAvB,uBAAuB;CAoEnC;AAED,qBAIa,8BAA+B,YAAW,SAAS;IACrD,qBAAqB,EAAE,qBAAqB,GAAG,IAAI,GAAG,SAAS,CAAC;IAEzE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiE;IAE9F,WAAW,IAAI,IAAI;yCANR,8BAA8B;2CAA9B,8BAA8B;CAmC1C;AAED,qBAIa,gCAAiC,YAAW,SAAS;IAM9D,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IANlB,uBAAuB,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,CAAC;IAE7E,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAsC;gBAGpD,UAAU,EAAE,UAAU,CAAC,WAAW,CAAC,EACnC,QAAQ,EAAE,SAAS;IAGtC,WAAW,IAAI,IAAI;IAkDnB,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,oBAAoB;yCAxEjB,gCAAgC;2CAAhC,gCAAgC;CA2E5C;AAED,qBAuKa,uBAAwB,YAAW,SAAS;IAC1C,OAAO,EAAE,qBAAqB,CAAU;IAC5C,OAAO,EAAE,iBAAiB,CAAU;IACpC,KAAK,EAAE,eAAe,CAAa;IACnC,MAAM,EAAE,gBAAgB,CAAY;IACpC,SAAS,UAAS;IAClB,eAAe,UAAS;IACxB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,eAAe,GAAG,SAAS,CAAC;IACtC,OAAO,EAAE,qBAAqB,GAAG,SAAS,CAAC;IAC3C,SAAS,EAAE,uBAAuB,GAAG,SAAS,CAAC;IAC/C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC;IAE1D,QAAQ,CAAC,SAAS,2BAAkC;IAE9D,gBAAgB,SAAM;IAEtB,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAM1C,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;yCAxBzB,uBAAuB;2CAAvB,uBAAuB;CA2BnC;AAED,qBAgBa,sBAAsB;yCAAtB,sBAAsB;0CAAtB,sBAAsB,+CApZtB,uBAAuB,SA0EvB,8BAA8B,SAyC9B,gCAAgC,SAoPhC,uBAAuB,yCAvWvB,uBAAuB,SA0EvB,8BAA8B,SAyC9B,gCAAgC,SAoPhC,uBAAuB;0CA6CvB,sBAAsB;CAAG"}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault } from "@angular/common";
|
||||
import { NgIf, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault } from "@angular/common";
|
||||
import { ChangeDetectionStrategy, Component, Directive, EventEmitter, inject, Input, NgModule, Output, } from "@angular/core";
|
||||
import { NzTypographyComponent, NzTypographyModule } from "ng-zorro-antd/typography";
|
||||
import { tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle, } from "../core";
|
||||
import { tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle, tachTypographyMarkdownToHtml, } from "../core";
|
||||
import * as i0 from "@angular/core";
|
||||
import * as i1 from "ng-zorro-antd/typography";
|
||||
const camelToKebab = (value) => value.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
|
||||
@@ -233,17 +233,25 @@ export class TachTypographyComponent {
|
||||
color = "primary";
|
||||
weight = "normal";
|
||||
clickable = false;
|
||||
markdownEnabled = false;
|
||||
markdown;
|
||||
className;
|
||||
ellipsis;
|
||||
nzProps;
|
||||
hostProps;
|
||||
preserveStyle;
|
||||
tachClick = new EventEmitter();
|
||||
renderedMarkdown = "";
|
||||
ngOnChanges(_changes) {
|
||||
this.renderedMarkdown = this.markdownEnabled
|
||||
? tachTypographyMarkdownToHtml(this.markdown ?? "")
|
||||
: "";
|
||||
}
|
||||
handleClick(event) {
|
||||
this.tachClick.emit(event);
|
||||
}
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: TachTypographyComponent, isStandalone: true, selector: "tach-typography", inputs: { hostTag: ["as", "hostTag"], variant: "variant", color: "color", weight: "weight", clickable: "clickable", className: "className", ellipsis: "ellipsis", nzProps: "nzProps", hostProps: "hostProps", preserveStyle: "preserveStyle" }, outputs: { tachClick: "tachClick" }, ngImport: i0, template: `
|
||||
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: TachTypographyComponent, isStandalone: true, selector: "tach-typography", inputs: { hostTag: ["as", "hostTag"], variant: "variant", color: "color", weight: "weight", clickable: "clickable", markdownEnabled: "markdownEnabled", markdown: "markdown", className: "className", ellipsis: "ellipsis", nzProps: "nzProps", hostProps: "hostProps", preserveStyle: "preserveStyle" }, outputs: { tachClick: "tachClick" }, usesOnChanges: true, ngImport: i0, template: `
|
||||
<ng-container [ngSwitch]="hostTag">
|
||||
<p
|
||||
*ngSwitchCase="'p'"
|
||||
@@ -259,7 +267,12 @@ export class TachTypographyComponent {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentP">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentP>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</p>
|
||||
<a
|
||||
*ngSwitchCase="'a'"
|
||||
@@ -275,7 +288,12 @@ export class TachTypographyComponent {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentA">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentA>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</a>
|
||||
<h1
|
||||
*ngSwitchCase="'h1'"
|
||||
@@ -291,7 +309,12 @@ export class TachTypographyComponent {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH1">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentH1>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</h1>
|
||||
<h2
|
||||
*ngSwitchCase="'h2'"
|
||||
@@ -307,7 +330,12 @@ export class TachTypographyComponent {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH2">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentH2>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</h2>
|
||||
<h3
|
||||
*ngSwitchCase="'h3'"
|
||||
@@ -323,7 +351,12 @@ export class TachTypographyComponent {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH3">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentH3>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</h3>
|
||||
<h4
|
||||
*ngSwitchCase="'h4'"
|
||||
@@ -339,7 +372,12 @@ export class TachTypographyComponent {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH4">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentH4>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</h4>
|
||||
<span
|
||||
*ngSwitchDefault
|
||||
@@ -355,10 +393,15 @@ export class TachTypographyComponent {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentSpan">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentSpan>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</span>
|
||||
</ng-container>
|
||||
`, isInline: true, dependencies: [{ kind: "ngmodule", type: NzTypographyModule }, { kind: "component", type: i1.NzTypographyComponent, selector: " nz-typography, [nz-typography], p[nz-paragraph], span[nz-text], h1[nz-title], h2[nz-title], h3[nz-title], h4[nz-title] ", inputs: ["nzCopyable", "nzEditable", "nzDisabled", "nzExpandable", "nzEllipsis", "nzCopyTooltips", "nzCopyIcons", "nzEditTooltip", "nzEditIcon", "nzContent", "nzEllipsisRows", "nzType", "nzCopyText", "nzSuffix"], outputs: ["nzContentChange", "nzCopy", "nzExpandChange", "nzOnEllipsis"], exportAs: ["nzTypography"] }, { kind: "directive", type: TachTypographyDirective, selector: "[tachTypography]", inputs: ["tachTypography", "tachTypographyVariant", "tachTypographyColor", "tachTypographyWeight", "tachTypographyClickable", "tachTypographyClassName", "tachTypographyEllipsis"] }, { kind: "directive", type: TachTypographyNzPropsDirective, selector: "[tachTypographyNzProps]", inputs: ["tachTypographyNzProps"] }, { kind: "directive", type: TachTypographyHostPropsDirective, selector: "[tachTypographyHostProps]", inputs: ["tachTypographyHostProps"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
||||
`, isInline: true, dependencies: [{ kind: "ngmodule", type: NzTypographyModule }, { kind: "component", type: i1.NzTypographyComponent, selector: " nz-typography, [nz-typography], p[nz-paragraph], span[nz-text], h1[nz-title], h2[nz-title], h3[nz-title], h4[nz-title] ", inputs: ["nzCopyable", "nzEditable", "nzDisabled", "nzExpandable", "nzEllipsis", "nzCopyTooltips", "nzCopyIcons", "nzEditTooltip", "nzEditIcon", "nzContent", "nzEllipsisRows", "nzType", "nzCopyText", "nzSuffix"], outputs: ["nzContentChange", "nzCopy", "nzExpandChange", "nzOnEllipsis"], exportAs: ["nzTypography"] }, { kind: "directive", type: TachTypographyDirective, selector: "[tachTypography]", inputs: ["tachTypography", "tachTypographyVariant", "tachTypographyColor", "tachTypographyWeight", "tachTypographyClickable", "tachTypographyClassName", "tachTypographyEllipsis"] }, { kind: "directive", type: TachTypographyNzPropsDirective, selector: "[tachTypographyNzProps]", inputs: ["tachTypographyNzProps"] }, { kind: "directive", type: TachTypographyHostPropsDirective, selector: "[tachTypographyHostProps]", inputs: ["tachTypographyHostProps"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyComponent, decorators: [{
|
||||
type: Component,
|
||||
@@ -373,6 +416,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
NgSwitchDefault,
|
||||
NgIf,
|
||||
NgStyle,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
@@ -392,7 +436,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentP">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentP>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</p>
|
||||
<a
|
||||
*ngSwitchCase="'a'"
|
||||
@@ -408,7 +457,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentA">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentA>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</a>
|
||||
<h1
|
||||
*ngSwitchCase="'h1'"
|
||||
@@ -424,7 +478,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH1">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentH1>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</h1>
|
||||
<h2
|
||||
*ngSwitchCase="'h2'"
|
||||
@@ -440,7 +499,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH2">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentH2>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</h2>
|
||||
<h3
|
||||
*ngSwitchCase="'h3'"
|
||||
@@ -456,7 +520,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH3">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentH3>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</h3>
|
||||
<h4
|
||||
*ngSwitchCase="'h4'"
|
||||
@@ -472,7 +541,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH4">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentH4>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</h4>
|
||||
<span
|
||||
*ngSwitchDefault
|
||||
@@ -488,7 +562,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentSpan">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentSpan>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</span>
|
||||
</ng-container>
|
||||
`,
|
||||
@@ -504,6 +583,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
||||
type: Input
|
||||
}], clickable: [{
|
||||
type: Input
|
||||
}], markdownEnabled: [{
|
||||
type: Input
|
||||
}], markdown: [{
|
||||
type: Input
|
||||
}], className: [{
|
||||
type: Input
|
||||
}], ellipsis: [{
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,5 @@
|
||||
export * from "./types";
|
||||
export * from "./classnames";
|
||||
export * from "./ellipsis";
|
||||
export * from "./markdown";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from "./types";
|
||||
export * from "./classnames";
|
||||
export * from "./ellipsis";
|
||||
export * from "./markdown";
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
|
||||
2
packages/tach-typography/dist/angular/core/markdown.d.ts
vendored
Normal file
2
packages/tach-typography/dist/angular/core/markdown.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export declare const tachTypographyMarkdownToHtml: (markdown: string) => string;
|
||||
//# sourceMappingURL=markdown.d.ts.map
|
||||
1
packages/tach-typography/dist/angular/core/markdown.d.ts.map
vendored
Normal file
1
packages/tach-typography/dist/angular/core/markdown.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../src/core/markdown.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,4BAA4B,GAAI,UAAU,MAAM,KAAG,MAyC/D,CAAC"}
|
||||
47
packages/tach-typography/dist/angular/core/markdown.js
vendored
Normal file
47
packages/tach-typography/dist/angular/core/markdown.js
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
const TOKEN_PREFIX = "TACHMDTOKEN";
|
||||
const SAFE_HREF_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
|
||||
const escapeHtml = (value) => value
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
const normalizeMarkdown = (value) => value.replace(/\r\n?/g, "\n");
|
||||
const sanitizeHref = (value) => {
|
||||
const href = value.trim();
|
||||
if (!href || !SAFE_HREF_PATTERN.test(href)) {
|
||||
return null;
|
||||
}
|
||||
return href;
|
||||
};
|
||||
export const tachTypographyMarkdownToHtml = (markdown) => {
|
||||
const source = normalizeMarkdown(markdown);
|
||||
const tokenMap = new Map();
|
||||
let tokenId = 0;
|
||||
const tokenized = source
|
||||
.replace(/`([^`\n]+)`/g, (_match, code) => {
|
||||
const token = `${TOKEN_PREFIX}${tokenId++}`;
|
||||
tokenMap.set(token, `<code class="tach-typography__md-code">${escapeHtml(code)}</code>`);
|
||||
return token;
|
||||
})
|
||||
.replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
|
||||
const token = `${TOKEN_PREFIX}${tokenId++}`;
|
||||
const href = sanitizeHref(hrefRaw);
|
||||
if (!href) {
|
||||
tokenMap.set(token, escapeHtml(label));
|
||||
return token;
|
||||
}
|
||||
tokenMap.set(token, `<a class="tach-typography__md-link" href="${escapeHtml(href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)}</a>`);
|
||||
return token;
|
||||
});
|
||||
let html = escapeHtml(tokenized)
|
||||
.replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>")
|
||||
.replace(/__([^_\n]+)__/g, "<strong>$1</strong>")
|
||||
.replace(/\*([^*\n]+)\*/g, "<em>$1</em>")
|
||||
.replace(/_([^_\n]+)_/g, "<em>$1</em>")
|
||||
.replace(/~~([^~\n]+)~~/g, "<del>$1</del>")
|
||||
.replace(/\n/g, "<br />");
|
||||
html = html.replace(new RegExp(`${TOKEN_PREFIX}\\d+`, "g"), token => tokenMap.get(token) || token);
|
||||
return html;
|
||||
};
|
||||
//# sourceMappingURL=markdown.js.map
|
||||
1
packages/tach-typography/dist/angular/core/markdown.js.map
vendored
Normal file
1
packages/tach-typography/dist/angular/core/markdown.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../../src/core/markdown.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG,aAAa,CAAC;AACnC,MAAM,iBAAiB,GAAG,+BAA+B,CAAC;AAE1D,MAAM,UAAU,GAAG,CAAC,KAAa,EAAU,EAAE,CAC3C,KAAK;KACF,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;KACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;KACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;KACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;KACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAE5B,MAAM,iBAAiB,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AAEnF,MAAM,YAAY,GAAG,CAAC,KAAa,EAAiB,EAAE;IACpD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAE1B,IAAI,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,QAAgB,EAAU,EAAE;IACvE,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,MAAM,SAAS,GAAG,MAAM;SACrB,OAAO,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE;QAChD,MAAM,KAAK,GAAG,GAAG,YAAY,GAAG,OAAO,EAAE,EAAE,CAAC;QAC5C,QAAQ,CAAC,GAAG,CACV,KAAK,EACL,0CAA0C,UAAU,CAAC,IAAI,CAAC,SAAS,CACpE,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;SACD,OAAO,CAAC,8BAA8B,EAAE,CAAC,MAAM,EAAE,KAAa,EAAE,OAAe,EAAE,EAAE;QAClF,MAAM,KAAK,GAAG,GAAG,YAAY,GAAG,OAAO,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QAEnC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;YACvC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,QAAQ,CAAC,GAAG,CACV,KAAK,EACL,6CAA6C,UAAU,CAAC,IAAI,CAAC,+CAA+C,UAAU,CAAC,KAAK,CAAC,MAAM,CACpI,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IAEL,IAAI,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC;SAC7B,OAAO,CAAC,oBAAoB,EAAE,qBAAqB,CAAC;SACpD,OAAO,CAAC,gBAAgB,EAAE,qBAAqB,CAAC;SAChD,OAAO,CAAC,gBAAgB,EAAE,aAAa,CAAC;SACxC,OAAO,CAAC,cAAc,EAAE,aAAa,CAAC;SACtC,OAAO,CAAC,gBAAgB,EAAE,eAAe,CAAC;SAC1C,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAE5B,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,GAAG,YAAY,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC;IAEnG,OAAO,IAAI,CAAC;AACd,CAAC,CAAC"}
|
||||
42
packages/tach-typography/dist/core/index.cjs
vendored
42
packages/tach-typography/dist/core/index.cjs
vendored
@@ -78,10 +78,52 @@ var tachTypographyEllipsisStyle = (ellipsis) => {
|
||||
};
|
||||
};
|
||||
|
||||
// src/core/markdown.ts
|
||||
var TOKEN_PREFIX = "TACHMDTOKEN";
|
||||
var SAFE_HREF_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
|
||||
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
||||
var normalizeMarkdown = (value) => value.replace(/\r\n?/g, "\n");
|
||||
var sanitizeHref = (value) => {
|
||||
const href = value.trim();
|
||||
if (!href || !SAFE_HREF_PATTERN.test(href)) {
|
||||
return null;
|
||||
}
|
||||
return href;
|
||||
};
|
||||
var tachTypographyMarkdownToHtml = (markdown) => {
|
||||
const source = normalizeMarkdown(markdown);
|
||||
const tokenMap = /* @__PURE__ */ new Map();
|
||||
let tokenId = 0;
|
||||
const tokenized = source.replace(/`([^`\n]+)`/g, (_match, code) => {
|
||||
const token = `${TOKEN_PREFIX}${tokenId++}`;
|
||||
tokenMap.set(
|
||||
token,
|
||||
`<code class="tach-typography__md-code">${escapeHtml(code)}</code>`
|
||||
);
|
||||
return token;
|
||||
}).replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
|
||||
const token = `${TOKEN_PREFIX}${tokenId++}`;
|
||||
const href = sanitizeHref(hrefRaw);
|
||||
if (!href) {
|
||||
tokenMap.set(token, escapeHtml(label));
|
||||
return token;
|
||||
}
|
||||
tokenMap.set(
|
||||
token,
|
||||
`<a class="tach-typography__md-link" href="${escapeHtml(href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)}</a>`
|
||||
);
|
||||
return token;
|
||||
});
|
||||
let html = escapeHtml(tokenized).replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>").replace(/__([^_\n]+)__/g, "<strong>$1</strong>").replace(/\*([^*\n]+)\*/g, "<em>$1</em>").replace(/_([^_\n]+)_/g, "<em>$1</em>").replace(/~~([^~\n]+)~~/g, "<del>$1</del>").replace(/\n/g, "<br />");
|
||||
html = html.replace(new RegExp(`${TOKEN_PREFIX}\\d+`, "g"), (token) => tokenMap.get(token) || token);
|
||||
return html;
|
||||
};
|
||||
|
||||
exports.TYPOGRAPHY_COLORS = TYPOGRAPHY_COLORS;
|
||||
exports.TYPOGRAPHY_VARIANTS = TYPOGRAPHY_VARIANTS;
|
||||
exports.tachTypographyClassList = tachTypographyClassList;
|
||||
exports.tachTypographyClassName = tachTypographyClassName;
|
||||
exports.tachTypographyEllipsisStyle = tachTypographyEllipsisStyle;
|
||||
exports.tachTypographyMarkdownToHtml = tachTypographyMarkdownToHtml;
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
File diff suppressed because one or more lines are too long
@@ -7,4 +7,6 @@ declare const tachTypographyClassList: (options?: TypographyClassOptions) => str
|
||||
type StyleRecord = Record<string, string | number>;
|
||||
declare const tachTypographyEllipsisStyle: (ellipsis?: EllipsisOptions) => StyleRecord | undefined;
|
||||
|
||||
export { EllipsisOptions, TypographyClassOptions, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle };
|
||||
declare const tachTypographyMarkdownToHtml: (markdown: string) => string;
|
||||
|
||||
export { EllipsisOptions, TypographyClassOptions, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle, tachTypographyMarkdownToHtml };
|
||||
|
||||
@@ -7,4 +7,6 @@ declare const tachTypographyClassList: (options?: TypographyClassOptions) => str
|
||||
type StyleRecord = Record<string, string | number>;
|
||||
declare const tachTypographyEllipsisStyle: (ellipsis?: EllipsisOptions) => StyleRecord | undefined;
|
||||
|
||||
export { EllipsisOptions, TypographyClassOptions, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle };
|
||||
declare const tachTypographyMarkdownToHtml: (markdown: string) => string;
|
||||
|
||||
export { EllipsisOptions, TypographyClassOptions, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle, tachTypographyMarkdownToHtml };
|
||||
|
||||
43
packages/tach-typography/dist/core/index.js
vendored
43
packages/tach-typography/dist/core/index.js
vendored
@@ -76,6 +76,47 @@ var tachTypographyEllipsisStyle = (ellipsis) => {
|
||||
};
|
||||
};
|
||||
|
||||
export { TYPOGRAPHY_COLORS, TYPOGRAPHY_VARIANTS, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle };
|
||||
// src/core/markdown.ts
|
||||
var TOKEN_PREFIX = "TACHMDTOKEN";
|
||||
var SAFE_HREF_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
|
||||
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
||||
var normalizeMarkdown = (value) => value.replace(/\r\n?/g, "\n");
|
||||
var sanitizeHref = (value) => {
|
||||
const href = value.trim();
|
||||
if (!href || !SAFE_HREF_PATTERN.test(href)) {
|
||||
return null;
|
||||
}
|
||||
return href;
|
||||
};
|
||||
var tachTypographyMarkdownToHtml = (markdown) => {
|
||||
const source = normalizeMarkdown(markdown);
|
||||
const tokenMap = /* @__PURE__ */ new Map();
|
||||
let tokenId = 0;
|
||||
const tokenized = source.replace(/`([^`\n]+)`/g, (_match, code) => {
|
||||
const token = `${TOKEN_PREFIX}${tokenId++}`;
|
||||
tokenMap.set(
|
||||
token,
|
||||
`<code class="tach-typography__md-code">${escapeHtml(code)}</code>`
|
||||
);
|
||||
return token;
|
||||
}).replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
|
||||
const token = `${TOKEN_PREFIX}${tokenId++}`;
|
||||
const href = sanitizeHref(hrefRaw);
|
||||
if (!href) {
|
||||
tokenMap.set(token, escapeHtml(label));
|
||||
return token;
|
||||
}
|
||||
tokenMap.set(
|
||||
token,
|
||||
`<a class="tach-typography__md-link" href="${escapeHtml(href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)}</a>`
|
||||
);
|
||||
return token;
|
||||
});
|
||||
let html = escapeHtml(tokenized).replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>").replace(/__([^_\n]+)__/g, "<strong>$1</strong>").replace(/\*([^*\n]+)\*/g, "<em>$1</em>").replace(/_([^_\n]+)_/g, "<em>$1</em>").replace(/~~([^~\n]+)~~/g, "<del>$1</del>").replace(/\n/g, "<br />");
|
||||
html = html.replace(new RegExp(`${TOKEN_PREFIX}\\d+`, "g"), (token) => tokenMap.get(token) || token);
|
||||
return html;
|
||||
};
|
||||
|
||||
export { TYPOGRAPHY_COLORS, TYPOGRAPHY_VARIANTS, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle, tachTypographyMarkdownToHtml };
|
||||
//# sourceMappingURL=index.js.map
|
||||
//# sourceMappingURL=index.js.map
|
||||
File diff suppressed because one or more lines are too long
61
packages/tach-typography/dist/react/index.cjs
vendored
61
packages/tach-typography/dist/react/index.cjs
vendored
@@ -29,9 +29,62 @@ var tachTypographyClassName = ({
|
||||
className
|
||||
);
|
||||
};
|
||||
|
||||
// src/core/markdown.ts
|
||||
var TOKEN_PREFIX = "TACHMDTOKEN";
|
||||
var SAFE_HREF_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
|
||||
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
||||
var normalizeMarkdown = (value) => value.replace(/\r\n?/g, "\n");
|
||||
var sanitizeHref = (value) => {
|
||||
const href = value.trim();
|
||||
if (!href || !SAFE_HREF_PATTERN.test(href)) {
|
||||
return null;
|
||||
}
|
||||
return href;
|
||||
};
|
||||
var tachTypographyMarkdownToHtml = (markdown) => {
|
||||
const source = normalizeMarkdown(markdown);
|
||||
const tokenMap = /* @__PURE__ */ new Map();
|
||||
let tokenId = 0;
|
||||
const tokenized = source.replace(/`([^`\n]+)`/g, (_match, code) => {
|
||||
const token = `${TOKEN_PREFIX}${tokenId++}`;
|
||||
tokenMap.set(
|
||||
token,
|
||||
`<code class="tach-typography__md-code">${escapeHtml(code)}</code>`
|
||||
);
|
||||
return token;
|
||||
}).replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
|
||||
const token = `${TOKEN_PREFIX}${tokenId++}`;
|
||||
const href = sanitizeHref(hrefRaw);
|
||||
if (!href) {
|
||||
tokenMap.set(token, escapeHtml(label));
|
||||
return token;
|
||||
}
|
||||
tokenMap.set(
|
||||
token,
|
||||
`<a class="tach-typography__md-link" href="${escapeHtml(href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)}</a>`
|
||||
);
|
||||
return token;
|
||||
});
|
||||
let html = escapeHtml(tokenized).replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>").replace(/__([^_\n]+)__/g, "<strong>$1</strong>").replace(/\*([^*\n]+)\*/g, "<em>$1</em>").replace(/_([^_\n]+)_/g, "<em>$1</em>").replace(/~~([^~\n]+)~~/g, "<del>$1</del>").replace(/\n/g, "<br />");
|
||||
html = html.replace(new RegExp(`${TOKEN_PREFIX}\\d+`, "g"), (token) => tokenMap.get(token) || token);
|
||||
return html;
|
||||
};
|
||||
var createTypographyVariant = (Component, variant) => {
|
||||
const Variant = React__default.default.forwardRef(
|
||||
({ color = "primary", weight = "normal", className, onClick, ...rest }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
||||
({
|
||||
color = "primary",
|
||||
weight = "normal",
|
||||
className,
|
||||
onClick,
|
||||
markdownEnabled = false,
|
||||
children,
|
||||
...rest
|
||||
}, ref) => {
|
||||
const markdownHtml = markdownEnabled && typeof children === "string" ? tachTypographyMarkdownToHtml(children) : void 0;
|
||||
const renderedChildren = markdownHtml ? /* @__PURE__ */ jsxRuntime.jsx("span", { dangerouslySetInnerHTML: { __html: markdownHtml } }) : children;
|
||||
const contentProps = { children: renderedChildren };
|
||||
return /* @__PURE__ */ jsxRuntime.jsx(
|
||||
Component,
|
||||
{
|
||||
ref,
|
||||
@@ -43,9 +96,11 @@ var createTypographyVariant = (Component, variant) => {
|
||||
clickable: Boolean(onClick)
|
||||
}),
|
||||
onClick,
|
||||
...rest
|
||||
...rest,
|
||||
...contentProps
|
||||
}
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
Variant.displayName = String(variant);
|
||||
return Variant;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -10,6 +10,8 @@ interface AdditionalProps {
|
||||
weight?: TypographyWeight;
|
||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
className?: string | undefined;
|
||||
markdownEnabled?: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
declare const TachTypography: {
|
||||
Text: {
|
||||
|
||||
@@ -10,6 +10,8 @@ interface AdditionalProps {
|
||||
weight?: TypographyWeight;
|
||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
className?: string | undefined;
|
||||
markdownEnabled?: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
declare const TachTypography: {
|
||||
Text: {
|
||||
|
||||
61
packages/tach-typography/dist/react/index.js
vendored
61
packages/tach-typography/dist/react/index.js
vendored
@@ -23,9 +23,62 @@ var tachTypographyClassName = ({
|
||||
className
|
||||
);
|
||||
};
|
||||
|
||||
// src/core/markdown.ts
|
||||
var TOKEN_PREFIX = "TACHMDTOKEN";
|
||||
var SAFE_HREF_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
|
||||
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
||||
var normalizeMarkdown = (value) => value.replace(/\r\n?/g, "\n");
|
||||
var sanitizeHref = (value) => {
|
||||
const href = value.trim();
|
||||
if (!href || !SAFE_HREF_PATTERN.test(href)) {
|
||||
return null;
|
||||
}
|
||||
return href;
|
||||
};
|
||||
var tachTypographyMarkdownToHtml = (markdown) => {
|
||||
const source = normalizeMarkdown(markdown);
|
||||
const tokenMap = /* @__PURE__ */ new Map();
|
||||
let tokenId = 0;
|
||||
const tokenized = source.replace(/`([^`\n]+)`/g, (_match, code) => {
|
||||
const token = `${TOKEN_PREFIX}${tokenId++}`;
|
||||
tokenMap.set(
|
||||
token,
|
||||
`<code class="tach-typography__md-code">${escapeHtml(code)}</code>`
|
||||
);
|
||||
return token;
|
||||
}).replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
|
||||
const token = `${TOKEN_PREFIX}${tokenId++}`;
|
||||
const href = sanitizeHref(hrefRaw);
|
||||
if (!href) {
|
||||
tokenMap.set(token, escapeHtml(label));
|
||||
return token;
|
||||
}
|
||||
tokenMap.set(
|
||||
token,
|
||||
`<a class="tach-typography__md-link" href="${escapeHtml(href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)}</a>`
|
||||
);
|
||||
return token;
|
||||
});
|
||||
let html = escapeHtml(tokenized).replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>").replace(/__([^_\n]+)__/g, "<strong>$1</strong>").replace(/\*([^*\n]+)\*/g, "<em>$1</em>").replace(/_([^_\n]+)_/g, "<em>$1</em>").replace(/~~([^~\n]+)~~/g, "<del>$1</del>").replace(/\n/g, "<br />");
|
||||
html = html.replace(new RegExp(`${TOKEN_PREFIX}\\d+`, "g"), (token) => tokenMap.get(token) || token);
|
||||
return html;
|
||||
};
|
||||
var createTypographyVariant = (Component, variant) => {
|
||||
const Variant = React.forwardRef(
|
||||
({ color = "primary", weight = "normal", className, onClick, ...rest }, ref) => /* @__PURE__ */ jsx(
|
||||
({
|
||||
color = "primary",
|
||||
weight = "normal",
|
||||
className,
|
||||
onClick,
|
||||
markdownEnabled = false,
|
||||
children,
|
||||
...rest
|
||||
}, ref) => {
|
||||
const markdownHtml = markdownEnabled && typeof children === "string" ? tachTypographyMarkdownToHtml(children) : void 0;
|
||||
const renderedChildren = markdownHtml ? /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: markdownHtml } }) : children;
|
||||
const contentProps = { children: renderedChildren };
|
||||
return /* @__PURE__ */ jsx(
|
||||
Component,
|
||||
{
|
||||
ref,
|
||||
@@ -37,9 +90,11 @@ var createTypographyVariant = (Component, variant) => {
|
||||
clickable: Boolean(onClick)
|
||||
}),
|
||||
onClick,
|
||||
...rest
|
||||
...rest,
|
||||
...contentProps
|
||||
}
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
Variant.displayName = String(variant);
|
||||
return Variant;
|
||||
|
||||
File diff suppressed because one or more lines are too long
11
packages/tach-typography/dist/styles.css
vendored
11
packages/tach-typography/dist/styles.css
vendored
@@ -88,6 +88,17 @@
|
||||
.tach-typography--AppMediumSubtextUnderline,
|
||||
.ant-typography.tach-typography--AppMediumSubtextUnderline { font-family: Inter, sans-serif; font-size: 11px; font-weight: 400; line-height: 17px; text-decoration: underline; }
|
||||
|
||||
.tach-typography .tach-typography__md-link,
|
||||
.ant-typography.tach-typography .tach-typography__md-link {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.tach-typography .tach-typography__md-code,
|
||||
.ant-typography.tach-typography .tach-typography__md-code {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
|
||||
@media (max-width: 575px) {
|
||||
.tach-typography--AccentLargeTtl,
|
||||
.ant-typography.tach-typography--AccentLargeTtl {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hublib-web/tach-typography",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"description": "Cross-framework typography package for React and Angular",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault } from "@angular/common";
|
||||
import { NgIf, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault } from "@angular/common";
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
tachTypographyClassList,
|
||||
tachTypographyClassName,
|
||||
tachTypographyEllipsisStyle,
|
||||
tachTypographyMarkdownToHtml,
|
||||
type EllipsisOptions,
|
||||
type TypographyClassOptions,
|
||||
type TypographyColor,
|
||||
@@ -291,6 +292,7 @@ export class TachTypographyHostPropsDirective implements OnChanges {
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
NgSwitchDefault,
|
||||
NgIf,
|
||||
NgStyle,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
@@ -310,7 +312,12 @@ export class TachTypographyHostPropsDirective implements OnChanges {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentP">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentP>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</p>
|
||||
<a
|
||||
*ngSwitchCase="'a'"
|
||||
@@ -326,7 +333,12 @@ export class TachTypographyHostPropsDirective implements OnChanges {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentA">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentA>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</a>
|
||||
<h1
|
||||
*ngSwitchCase="'h1'"
|
||||
@@ -342,7 +354,12 @@ export class TachTypographyHostPropsDirective implements OnChanges {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH1">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentH1>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</h1>
|
||||
<h2
|
||||
*ngSwitchCase="'h2'"
|
||||
@@ -358,7 +375,12 @@ export class TachTypographyHostPropsDirective implements OnChanges {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH2">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentH2>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</h2>
|
||||
<h3
|
||||
*ngSwitchCase="'h3'"
|
||||
@@ -374,7 +396,12 @@ export class TachTypographyHostPropsDirective implements OnChanges {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH3">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentH3>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</h3>
|
||||
<h4
|
||||
*ngSwitchCase="'h4'"
|
||||
@@ -390,7 +417,12 @@ export class TachTypographyHostPropsDirective implements OnChanges {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH4">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentH4>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</h4>
|
||||
<span
|
||||
*ngSwitchDefault
|
||||
@@ -406,17 +438,24 @@ export class TachTypographyHostPropsDirective implements OnChanges {
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container *ngIf="markdownEnabled; else tachTypographyContentSpan">
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-container>
|
||||
<ng-template #tachTypographyContentSpan>
|
||||
<ng-content />
|
||||
</ng-template>
|
||||
</span>
|
||||
</ng-container>
|
||||
`,
|
||||
})
|
||||
export class TachTypographyComponent {
|
||||
export class TachTypographyComponent implements OnChanges {
|
||||
@Input("as") hostTag: TachTypographyHostTag = "span";
|
||||
@Input() variant: TypographyVariant = "Body";
|
||||
@Input() color: TypographyColor = "primary";
|
||||
@Input() weight: TypographyWeight = "normal";
|
||||
@Input() clickable = false;
|
||||
@Input() markdownEnabled = false;
|
||||
@Input() markdown: string | undefined;
|
||||
@Input() className: string | undefined;
|
||||
@Input() ellipsis: EllipsisOptions | undefined;
|
||||
@Input() nzProps: TachTypographyNzProps | undefined;
|
||||
@@ -425,6 +464,14 @@ export class TachTypographyComponent {
|
||||
|
||||
@Output() readonly tachClick = new EventEmitter<MouseEvent>();
|
||||
|
||||
renderedMarkdown = "";
|
||||
|
||||
ngOnChanges(_changes: SimpleChanges): void {
|
||||
this.renderedMarkdown = this.markdownEnabled
|
||||
? tachTypographyMarkdownToHtml(this.markdown ?? "")
|
||||
: "";
|
||||
}
|
||||
|
||||
handleClick(event: MouseEvent): void {
|
||||
this.tachClick.emit(event);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user