release(content-suggestions): v0.2.0

This commit is contained in:
2026-04-03 16:10:45 +03:00
parent cacbc016ec
commit d879af2881
73 changed files with 2369 additions and 1978 deletions

View 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

View File

@@ -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"}

View 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

View File

@@ -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"}

View 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

View 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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"}

View 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

File diff suppressed because one or more lines are too long

View 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

View File

@@ -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"}

View 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

View File

@@ -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"}

View 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

View 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"}

View 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

View 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"}

View File

@@ -0,0 +1,2 @@
export * from "./index";
//# sourceMappingURL=public-api.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../../../src/angular/public-api.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC"}

View File

@@ -0,0 +1,2 @@
export * from "./index";
//# sourceMappingURL=public-api.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../../src/angular/public-api.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC"}

View 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

View 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"}

View 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

View 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"}

View 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

View 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"}

View 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

View 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"}

View 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

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/angular/types.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/angular/types.ts"],"names":[],"mappings":""}

View 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

View 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"}

View File

@@ -0,0 +1,2 @@
export { findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent, } from "./parser";
//# sourceMappingURL=index.js.map

View 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"}

View 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

View 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"}

View 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

View File

@@ -0,0 +1 @@
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../src/core/parser.ts"],"names":[],"mappings":"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"}

View 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

View 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"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/types.ts"],"names":[],"mappings":""}

View File

@@ -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

View File

@@ -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 };

View File

@@ -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";

View File

@@ -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