Compare commits
13 Commits
video-play
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bb8f9c697 | |||
| 74f11aefbc | |||
| 28b91cd9eb | |||
| d879af2881 | |||
| cacbc016ec | |||
| a8c2eaa2fd | |||
| 9eaca089e5 | |||
| 8ac6e00b68 | |||
| 81c5550311 | |||
| 1c1b9748c6 | |||
| 6c73b0fa61 | |||
| e4e6bc5af4 | |||
| 028ce21c4c |
@@ -1,7 +1,9 @@
|
||||
import js from "@eslint/js";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
files: ["**/*.ts", "**/*.tsx"],
|
||||
languageOptions: {
|
||||
@@ -9,7 +11,8 @@ export default [
|
||||
sourceType: "module",
|
||||
},
|
||||
rules: {
|
||||
"no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"prettier": "^3.6.2",
|
||||
"storybook": "8.6.14",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.57.0",
|
||||
"vitest": "^3.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,10 @@ Cross-framework content text/title renderer with support for mentions, tags and
|
||||
- `ContentText`
|
||||
- `ContentTextWithSuggestions`
|
||||
- `ContentTitleWithSuggestions`
|
||||
- Angular adapter in `angular` for rendering/tokenization integration.
|
||||
- Angular UI components in `angular` (based on `@hublib-web/tach-typography/angular`):
|
||||
- `ContentTextComponent`
|
||||
- `ContentTextWithSuggestionsComponent`
|
||||
- `ContentTitleWithSuggestionsComponent`
|
||||
- Depends on `@hublib-web/tach-typography` for visual consistency.
|
||||
- Business logic (API requests for mentions/tags) stays in consumer application.
|
||||
|
||||
@@ -19,7 +22,7 @@ Cross-framework content text/title renderer with support for mentions, tags and
|
||||
yarn add "@hublib-web/content-suggestions@git+ssh://git@github.com/ORG/REPO.git#workspace=@hublib-web/content-suggestions&tag=content-suggestions-v0.1.0"
|
||||
```
|
||||
|
||||
`@hublib-web/tach-typography` is a peer dependency, so install a compatible tag for it as well.
|
||||
`@hublib-web/tach-typography@0.3.0` is a peer dependency.
|
||||
|
||||
## Install inside this monorepo
|
||||
|
||||
@@ -77,6 +80,43 @@ By default, tags are rendered as plain styled text (not links).
|
||||
|
||||
## Angular usage
|
||||
|
||||
Standalone component usage:
|
||||
|
||||
```ts
|
||||
import { Component } from "@angular/core";
|
||||
import { ContentTextWithSuggestionsComponent } from "@hublib-web/content-suggestions/angular";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [ContentTextWithSuggestionsComponent],
|
||||
template: `
|
||||
<content-text-with-suggestions
|
||||
[text]="text"
|
||||
[ellipsis]="{ count: 180, expandable: true }"
|
||||
[onView]="onView"
|
||||
/>
|
||||
`,
|
||||
})
|
||||
export class FeedPostComponent {
|
||||
text = "@[John Doe](d290f1ee-6c54-4b01-90e6-d701748f0851) hello #tag";
|
||||
|
||||
onView = () => {
|
||||
// analytics
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Title variant with defaults (`weight: "bold"`, `ellipsis: { rows: 2 }`):
|
||||
|
||||
```ts
|
||||
import { ContentTitleWithSuggestionsComponent } from "@hublib-web/content-suggestions/angular";
|
||||
|
||||
// template
|
||||
// <content-title-with-suggestions [text]="title" />
|
||||
```
|
||||
|
||||
Tokenization helper:
|
||||
|
||||
```ts
|
||||
import { createAngularContentTokens } from "@hublib-web/content-suggestions/angular";
|
||||
|
||||
|
||||
21
packages/content-suggestions/dist/angular/angular/content-text-with-suggestions.component.d.ts
vendored
Normal file
21
packages/content-suggestions/dist/angular/angular/content-text-with-suggestions.component.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { AngularContentEllipsisConfig, AngularLinkRenderer, AngularMentionRenderer, AngularTagRenderer } from "./types";
|
||||
import { EventEmitter } from "@angular/core";
|
||||
import type { TypographyWeight } from "@hublib-web/tach-typography/core";
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class ContentTextWithSuggestionsComponent {
|
||||
className: string | undefined;
|
||||
weight: TypographyWeight;
|
||||
text: string | null | undefined;
|
||||
ellipsis: AngularContentEllipsisConfig;
|
||||
blur: boolean;
|
||||
style: Record<string, string | number> | null | undefined;
|
||||
onView: (() => void) | undefined;
|
||||
renderMention: AngularMentionRenderer | undefined;
|
||||
renderTag: AngularTagRenderer | undefined;
|
||||
renderLink: AngularLinkRenderer | undefined;
|
||||
readonly viewed: EventEmitter<void>;
|
||||
readonly expandedChange: EventEmitter<boolean>;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<ContentTextWithSuggestionsComponent, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<ContentTextWithSuggestionsComponent, "content-text-with-suggestions", never, { "className": { "alias": "className"; "required": false; }; "weight": { "alias": "weight"; "required": false; }; "text": { "alias": "text"; "required": false; }; "ellipsis": { "alias": "ellipsis"; "required": false; }; "blur": { "alias": "blur"; "required": false; }; "style": { "alias": "style"; "required": false; }; "onView": { "alias": "onView"; "required": false; }; "renderMention": { "alias": "renderMention"; "required": false; }; "renderTag": { "alias": "renderTag"; "required": false; }; "renderLink": { "alias": "renderLink"; "required": false; }; }, { "viewed": "viewed"; "expandedChange": "expandedChange"; }, never, never, true, never>;
|
||||
}
|
||||
//# sourceMappingURL=content-text-with-suggestions.component.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"content-text-with-suggestions.component.d.ts","sourceRoot":"","sources":["../../../src/angular/content-text-with-suggestions.component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,4BAA4B,EAC5B,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAsC,YAAY,EAAiB,MAAM,eAAe,CAAC;AAEhG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;;AAIzE,qBAsBa,mCAAmC;IACrC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,gBAAgB,CAAY;IACpC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,4BAA4B,CAAS;IAC/C,IAAI,UAAS;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1D,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAClD,SAAS,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC1C,UAAU,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAE3C,QAAQ,CAAC,MAAM,qBAA4B;IAC3C,QAAQ,CAAC,cAAc,wBAA+B;yCAbrD,mCAAmC;2CAAnC,mCAAmC;CAc/C"}
|
||||
84
packages/content-suggestions/dist/angular/angular/content-text-with-suggestions.component.js
vendored
Normal file
84
packages/content-suggestions/dist/angular/angular/content-text-with-suggestions.component.js
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
import { ContentTextComponent } from "./content-text.component";
|
||||
import * as i0 from "@angular/core";
|
||||
export class ContentTextWithSuggestionsComponent {
|
||||
className;
|
||||
weight = "normal";
|
||||
text;
|
||||
ellipsis = false;
|
||||
blur = false;
|
||||
style;
|
||||
onView;
|
||||
renderMention;
|
||||
renderTag;
|
||||
renderLink;
|
||||
viewed = new EventEmitter();
|
||||
expandedChange = new EventEmitter();
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentTextWithSuggestionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: ContentTextWithSuggestionsComponent, isStandalone: true, selector: "content-text-with-suggestions", inputs: { className: "className", weight: "weight", text: "text", ellipsis: "ellipsis", blur: "blur", style: "style", onView: "onView", renderMention: "renderMention", renderTag: "renderTag", renderLink: "renderLink" }, outputs: { viewed: "viewed", expandedChange: "expandedChange" }, ngImport: i0, template: `
|
||||
<content-text
|
||||
[className]="className"
|
||||
[weight]="weight"
|
||||
[text]="text"
|
||||
[ellipsis]="ellipsis"
|
||||
[blur]="blur"
|
||||
[style]="style"
|
||||
[onView]="onView"
|
||||
[renderMention]="renderMention"
|
||||
[renderTag]="renderTag"
|
||||
[renderLink]="renderLink"
|
||||
(viewed)="viewed.emit()"
|
||||
(expandedChange)="expandedChange.emit($event)"
|
||||
></content-text>
|
||||
`, 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)"
|
||||
></content-text>
|
||||
`,
|
||||
}]
|
||||
}], propDecorators: { className: [{
|
||||
type: Input
|
||||
}], weight: [{
|
||||
type: Input
|
||||
}], text: [{
|
||||
type: Input
|
||||
}], ellipsis: [{
|
||||
type: Input
|
||||
}], blur: [{
|
||||
type: Input
|
||||
}], style: [{
|
||||
type: Input
|
||||
}], onView: [{
|
||||
type: Input
|
||||
}], renderMention: [{
|
||||
type: Input
|
||||
}], renderTag: [{
|
||||
type: Input
|
||||
}], renderLink: [{
|
||||
type: Input
|
||||
}], viewed: [{
|
||||
type: Output
|
||||
}], expandedChange: [{
|
||||
type: Output
|
||||
}] } });
|
||||
//# sourceMappingURL=content-text-with-suggestions.component.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"content-text-with-suggestions.component.js","sourceRoot":"","sources":["../../../src/angular/content-text-with-suggestions.component.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAIhG,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;;AAwBhE,MAAM,OAAO,mCAAmC;IACrC,SAAS,CAAqB;IAC9B,MAAM,GAAqB,QAAQ,CAAC;IACpC,IAAI,CAA4B;IAChC,QAAQ,GAAiC,KAAK,CAAC;IAC/C,IAAI,GAAG,KAAK,CAAC;IACb,KAAK,CAAqD;IAC1D,MAAM,CAA2B;IACjC,aAAa,CAAqC;IAClD,SAAS,CAAiC;IAC1C,UAAU,CAAkC;IAElC,MAAM,GAAG,IAAI,YAAY,EAAQ,CAAC;IAClC,cAAc,GAAG,IAAI,YAAY,EAAW,CAAC;wGAbrD,mCAAmC;4FAAnC,mCAAmC,sXAjBpC;;;;;;;;;;;;;;;GAeT,4DAjBS,oBAAoB;;4FAmBnB,mCAAmC;kBAtB/C,SAAS;mBAAC;oBACT,QAAQ,EAAE,+BAA+B;oBACzC,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,oBAAoB,CAAC;oBAC/B,eAAe,EAAE,uBAAuB,CAAC,MAAM;oBAC/C,QAAQ,EAAE;;;;;;;;;;;;;;;GAeT;iBACF;;sBAEE,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBAEL,MAAM;;sBACN,MAAM"}
|
||||
92
packages/content-suggestions/dist/angular/angular/content-text.component.d.ts
vendored
Normal file
92
packages/content-suggestions/dist/angular/angular/content-text.component.d.ts
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
import type { LinkEntity, MentionEntity, TagEntity } from "../core";
|
||||
import type { AngularContentEllipsisConfig, AngularLinkRenderer, AngularRenderResult, AngularTagRenderer, AngularMentionRenderer } from "./types";
|
||||
import { AfterViewInit, ChangeDetectorRef, EventEmitter, OnChanges, OnDestroy, SimpleChanges } from "@angular/core";
|
||||
import { type TachTypographyHostProps } from "@hublib-web/tach-typography/angular";
|
||||
import type { TypographyWeight } from "@hublib-web/tach-typography/core";
|
||||
import * as i0 from "@angular/core";
|
||||
type TextPart = {
|
||||
kind: "text";
|
||||
key: string;
|
||||
text: string;
|
||||
};
|
||||
type MentionPart = {
|
||||
kind: "mention";
|
||||
key: string;
|
||||
entity: MentionEntity;
|
||||
index: number;
|
||||
customRender: AngularRenderResult | null;
|
||||
};
|
||||
type TagPart = {
|
||||
kind: "tag";
|
||||
key: string;
|
||||
entity: TagEntity;
|
||||
index: number;
|
||||
customRender: AngularRenderResult | null;
|
||||
};
|
||||
type LinkPart = {
|
||||
kind: "link";
|
||||
key: string;
|
||||
entity: LinkEntity;
|
||||
index: number;
|
||||
customRender: AngularRenderResult | null;
|
||||
};
|
||||
type ContentPart = TextPart | MentionPart | TagPart | LinkPart;
|
||||
export declare class ContentTextComponent implements OnChanges, AfterViewInit, OnDestroy {
|
||||
private readonly cdr;
|
||||
className: string | undefined;
|
||||
weight: TypographyWeight;
|
||||
text: string | null | undefined;
|
||||
ellipsis: AngularContentEllipsisConfig;
|
||||
blur: boolean;
|
||||
style: Record<string, string | number> | null | undefined;
|
||||
onView: (() => void) | undefined;
|
||||
renderMention: AngularMentionRenderer | undefined;
|
||||
renderTag: AngularTagRenderer | undefined;
|
||||
renderLink: AngularLinkRenderer | undefined;
|
||||
readonly viewed: EventEmitter<void>;
|
||||
readonly expandedChange: EventEmitter<boolean>;
|
||||
private readonly containerRef;
|
||||
visibleParts: ContentPart[];
|
||||
mergedStyle: Record<string, string | number>;
|
||||
rowsEllipsis: {
|
||||
rows: number;
|
||||
} | undefined;
|
||||
showTrailingEllipsis: boolean;
|
||||
showInlineReadMore: boolean;
|
||||
showBlockReadMore: boolean;
|
||||
readMoreLabel: string;
|
||||
paragraphHostProps: TachTypographyHostProps;
|
||||
private expanded;
|
||||
private mergedExpanded;
|
||||
private isExpandedControlled;
|
||||
private isInsideView;
|
||||
private viewedFired;
|
||||
private pendingRowsOverflowCheck;
|
||||
private intersectionObserver;
|
||||
constructor(cdr: ChangeDetectorRef);
|
||||
ngOnChanges(_changes: SimpleChanges): void;
|
||||
ngAfterViewInit(): void;
|
||||
ngOnDestroy(): void;
|
||||
trackByKey(_index: number, part: ContentPart): string;
|
||||
buildDefaultLinkHostProps(url: string): TachTypographyHostProps;
|
||||
handleExpand(event: MouseEvent): void;
|
||||
private recomputeContent;
|
||||
private buildParts;
|
||||
private resolveCustomRender;
|
||||
private resolveEllipsisConfig;
|
||||
private isCountConfig;
|
||||
private isRowsConfig;
|
||||
private getMergedExpanded;
|
||||
private resolveEllipsisSymbol;
|
||||
private notifyExpand;
|
||||
private computeEntitySafeCutoff;
|
||||
private initObserver;
|
||||
private emitViewIfNeeded;
|
||||
private scheduleRowsOverflowCheck;
|
||||
private refreshRowsOverflow;
|
||||
private getParagraphElement;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<ContentTextComponent, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<ContentTextComponent, "content-text", never, { "className": { "alias": "className"; "required": false; }; "weight": { "alias": "weight"; "required": false; }; "text": { "alias": "text"; "required": false; }; "ellipsis": { "alias": "ellipsis"; "required": false; }; "blur": { "alias": "blur"; "required": false; }; "style": { "alias": "style"; "required": false; }; "onView": { "alias": "onView"; "required": false; }; "renderMention": { "alias": "renderMention"; "required": false; }; "renderTag": { "alias": "renderTag"; "required": false; }; "renderLink": { "alias": "renderLink"; "required": false; }; }, { "viewed": "viewed"; "expandedChange": "expandedChange"; }, never, never, true, never>;
|
||||
}
|
||||
export {};
|
||||
//# sourceMappingURL=content-text.component.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/angular/content-text.component.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/content-text.component.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"content-text.component.d.ts","sourceRoot":"","sources":["../../../src/angular/content-text.component.ts"],"names":[],"mappings":"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"}
|
||||
536
packages/content-suggestions/dist/angular/angular/content-text.component.js
vendored
Normal file
536
packages/content-suggestions/dist/angular/angular/content-text.component.js
vendored
Normal file
@@ -0,0 +1,536 @@
|
||||
import { NgFor, NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault } from "@angular/common";
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewChild, } from "@angular/core";
|
||||
import { TachTypographyComponent, } from "@hublib-web/tach-typography/angular";
|
||||
import { findAllEntities } from "../core";
|
||||
import { ContentSuggestionsRenderResultDirective } from "./render-result.directive";
|
||||
import * as i0 from "@angular/core";
|
||||
const READ_MORE_TEXT = "Читать полностью";
|
||||
const BODY_DATA_ATTR = "data-content-suggestions-body";
|
||||
const BASE_SELECTABLE_STYLE = {
|
||||
whiteSpace: "pre-wrap",
|
||||
WebkitTouchCallout: "default",
|
||||
WebkitUserSelect: "text",
|
||||
KhtmlUserSelect: "text",
|
||||
MozUserSelect: "text",
|
||||
msUserSelect: "text",
|
||||
userSelect: "text",
|
||||
};
|
||||
const BLUR_STYLE = {
|
||||
filter: "blur(3px)",
|
||||
WebkitUserSelect: "none",
|
||||
KhtmlUserSelect: "none",
|
||||
MozUserSelect: "none",
|
||||
msUserSelect: "none",
|
||||
userSelect: "none",
|
||||
pointerEvents: "none",
|
||||
};
|
||||
export class ContentTextComponent {
|
||||
cdr;
|
||||
className;
|
||||
weight = "normal";
|
||||
text;
|
||||
ellipsis = false;
|
||||
blur = false;
|
||||
style;
|
||||
onView;
|
||||
renderMention;
|
||||
renderTag;
|
||||
renderLink;
|
||||
viewed = new EventEmitter();
|
||||
expandedChange = new EventEmitter();
|
||||
containerRef;
|
||||
visibleParts = [];
|
||||
mergedStyle = { ...BASE_SELECTABLE_STYLE };
|
||||
rowsEllipsis;
|
||||
showTrailingEllipsis = false;
|
||||
showInlineReadMore = false;
|
||||
showBlockReadMore = false;
|
||||
readMoreLabel = READ_MORE_TEXT;
|
||||
paragraphHostProps = { [BODY_DATA_ATTR]: "true" };
|
||||
expanded = false;
|
||||
mergedExpanded = false;
|
||||
isExpandedControlled = false;
|
||||
isInsideView = false;
|
||||
viewedFired = false;
|
||||
pendingRowsOverflowCheck = false;
|
||||
intersectionObserver = null;
|
||||
constructor(cdr) {
|
||||
this.cdr = cdr;
|
||||
}
|
||||
ngOnChanges(_changes) {
|
||||
this.recomputeContent();
|
||||
}
|
||||
ngAfterViewInit() {
|
||||
this.initObserver();
|
||||
this.scheduleRowsOverflowCheck();
|
||||
}
|
||||
ngOnDestroy() {
|
||||
if (this.intersectionObserver) {
|
||||
this.intersectionObserver.disconnect();
|
||||
this.intersectionObserver = null;
|
||||
}
|
||||
}
|
||||
trackByKey(_index, part) {
|
||||
return part.key;
|
||||
}
|
||||
buildDefaultLinkHostProps(url) {
|
||||
return {
|
||||
href: url,
|
||||
target: "_blank",
|
||||
referrerPolicy: "no-referrer",
|
||||
};
|
||||
}
|
||||
handleExpand(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!this.isExpandedControlled) {
|
||||
this.expanded = true;
|
||||
}
|
||||
this.notifyExpand(event, true);
|
||||
this.recomputeContent();
|
||||
}
|
||||
recomputeContent() {
|
||||
const content = this.text ?? "";
|
||||
const entities = findAllEntities(content);
|
||||
const ellipsisConfig = this.resolveEllipsisConfig();
|
||||
this.isExpandedControlled = Boolean(ellipsisConfig && typeof ellipsisConfig.expanded === "boolean");
|
||||
if (!this.isExpandedControlled && this.isCountConfig(ellipsisConfig)) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
if (content.length <= count && !this.expanded) {
|
||||
this.expanded = true;
|
||||
}
|
||||
}
|
||||
this.mergedExpanded = this.getMergedExpanded(ellipsisConfig, content);
|
||||
this.readMoreLabel = this.resolveEllipsisSymbol(ellipsisConfig?.symbol, this.mergedExpanded);
|
||||
this.mergedStyle = {
|
||||
...BASE_SELECTABLE_STYLE,
|
||||
...(this.blur ? BLUR_STYLE : {}),
|
||||
...(this.style ?? {}),
|
||||
};
|
||||
this.visibleParts = this.buildParts(content, entities, null);
|
||||
this.rowsEllipsis = undefined;
|
||||
this.showTrailingEllipsis = false;
|
||||
this.showInlineReadMore = false;
|
||||
this.showBlockReadMore = false;
|
||||
this.pendingRowsOverflowCheck = false;
|
||||
if (this.isCountConfig(ellipsisConfig)) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
const shouldTruncate = !this.mergedExpanded && count > 0 && content.length > count;
|
||||
if (shouldTruncate) {
|
||||
const cutoff = this.computeEntitySafeCutoff(count, entities);
|
||||
this.visibleParts = this.buildParts(content, entities, cutoff);
|
||||
this.showTrailingEllipsis = true;
|
||||
this.showInlineReadMore = Boolean(ellipsisConfig.expandable);
|
||||
}
|
||||
}
|
||||
else if (this.isRowsConfig(ellipsisConfig) && !this.mergedExpanded) {
|
||||
this.rowsEllipsis = {
|
||||
rows: ellipsisConfig.rows,
|
||||
};
|
||||
if (ellipsisConfig.expandable) {
|
||||
this.pendingRowsOverflowCheck = true;
|
||||
}
|
||||
}
|
||||
this.emitViewIfNeeded();
|
||||
this.scheduleRowsOverflowCheck();
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
buildParts(content, entities, upto) {
|
||||
const parts = [];
|
||||
let lastIndex = 0;
|
||||
for (const [index, entity] of entities.entries()) {
|
||||
if (upto !== null && entity.start >= upto) {
|
||||
break;
|
||||
}
|
||||
const textEnd = upto !== null ? Math.min(entity.start, upto) : entity.start;
|
||||
if (entity.start > lastIndex && lastIndex < textEnd) {
|
||||
parts.push({
|
||||
kind: "text",
|
||||
key: `text-${lastIndex}-${textEnd}`,
|
||||
text: content.slice(lastIndex, textEnd),
|
||||
});
|
||||
}
|
||||
if (upto === null || entity.end <= upto) {
|
||||
if (entity.type === "mention") {
|
||||
parts.push({
|
||||
kind: "mention",
|
||||
key: `mention-${entity.start}-${index}`,
|
||||
entity,
|
||||
index,
|
||||
customRender: this.resolveCustomRender(this.renderMention, entity, index),
|
||||
});
|
||||
}
|
||||
else if (entity.type === "tag") {
|
||||
parts.push({
|
||||
kind: "tag",
|
||||
key: `tag-${entity.start}-${index}`,
|
||||
entity,
|
||||
index,
|
||||
customRender: this.resolveCustomRender(this.renderTag, entity, index),
|
||||
});
|
||||
}
|
||||
else {
|
||||
parts.push({
|
||||
kind: "link",
|
||||
key: `link-${entity.start}-${index}`,
|
||||
entity,
|
||||
index,
|
||||
customRender: this.resolveCustomRender(this.renderLink, entity, index),
|
||||
});
|
||||
}
|
||||
}
|
||||
lastIndex = entity.end;
|
||||
}
|
||||
if (upto === null) {
|
||||
if (lastIndex < content.length) {
|
||||
parts.push({
|
||||
kind: "text",
|
||||
key: `text-${lastIndex}-${content.length}`,
|
||||
text: content.slice(lastIndex),
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (lastIndex < upto) {
|
||||
parts.push({
|
||||
kind: "text",
|
||||
key: `text-${lastIndex}-${upto}`,
|
||||
text: content.slice(lastIndex, upto),
|
||||
});
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
resolveCustomRender(rendererFn, entity, index) {
|
||||
const rendered = rendererFn?.(entity, index);
|
||||
return rendered ?? null;
|
||||
}
|
||||
resolveEllipsisConfig() {
|
||||
if (!this.ellipsis || typeof this.ellipsis !== "object") {
|
||||
return null;
|
||||
}
|
||||
return this.ellipsis;
|
||||
}
|
||||
isCountConfig(config) {
|
||||
return Boolean(config && "count" in config);
|
||||
}
|
||||
isRowsConfig(config) {
|
||||
return Boolean(config && "rows" in config);
|
||||
}
|
||||
getMergedExpanded(config, text) {
|
||||
const controlledExpanded = config && typeof config.expanded === "boolean"
|
||||
? config.expanded
|
||||
: undefined;
|
||||
if (controlledExpanded !== undefined) {
|
||||
return controlledExpanded;
|
||||
}
|
||||
if (this.isCountConfig(config)) {
|
||||
const count = config.count ?? 0;
|
||||
if (count <= 0 || text.length <= count) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return this.expanded;
|
||||
}
|
||||
resolveEllipsisSymbol(symbol, expanded) {
|
||||
if (typeof symbol === "function") {
|
||||
return symbol(expanded);
|
||||
}
|
||||
return symbol ?? READ_MORE_TEXT;
|
||||
}
|
||||
notifyExpand(event, expanded) {
|
||||
const config = this.resolveEllipsisConfig();
|
||||
const onExpand = config?.onExpand;
|
||||
if (onExpand) {
|
||||
if (onExpand.length >= 2) {
|
||||
onExpand(event, { expanded });
|
||||
}
|
||||
else {
|
||||
onExpand(expanded);
|
||||
}
|
||||
}
|
||||
this.expandedChange.emit(expanded);
|
||||
}
|
||||
computeEntitySafeCutoff(count, entities) {
|
||||
let cutoff = count;
|
||||
let extended = true;
|
||||
while (extended) {
|
||||
extended = false;
|
||||
for (const entity of entities) {
|
||||
if (entity.start < cutoff && entity.end > cutoff) {
|
||||
cutoff = entity.end;
|
||||
extended = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cutoff;
|
||||
}
|
||||
initObserver() {
|
||||
const host = this.containerRef?.nativeElement;
|
||||
if (!host) {
|
||||
return;
|
||||
}
|
||||
if (typeof IntersectionObserver === "undefined") {
|
||||
this.isInsideView = true;
|
||||
this.emitViewIfNeeded();
|
||||
return;
|
||||
}
|
||||
this.intersectionObserver = new IntersectionObserver(entries => {
|
||||
const entry = entries[0];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
if (entry.isIntersecting && !this.isInsideView) {
|
||||
this.isInsideView = true;
|
||||
this.emitViewIfNeeded();
|
||||
}
|
||||
}, { threshold: 0.5 });
|
||||
this.intersectionObserver.observe(host);
|
||||
}
|
||||
emitViewIfNeeded() {
|
||||
if (!this.mergedExpanded || this.viewedFired || !this.isInsideView) {
|
||||
return;
|
||||
}
|
||||
this.viewedFired = true;
|
||||
this.onView?.();
|
||||
this.viewed.emit();
|
||||
}
|
||||
scheduleRowsOverflowCheck() {
|
||||
if (!this.pendingRowsOverflowCheck) {
|
||||
return;
|
||||
}
|
||||
queueMicrotask(() => {
|
||||
this.refreshRowsOverflow();
|
||||
});
|
||||
}
|
||||
refreshRowsOverflow() {
|
||||
const paragraph = this.getParagraphElement();
|
||||
if (!paragraph) {
|
||||
return;
|
||||
}
|
||||
const hasOverflow = paragraph.scrollHeight - paragraph.clientHeight > 1;
|
||||
if (this.showBlockReadMore !== hasOverflow) {
|
||||
this.showBlockReadMore = hasOverflow;
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
}
|
||||
getParagraphElement() {
|
||||
const host = this.containerRef?.nativeElement;
|
||||
if (!host) {
|
||||
return null;
|
||||
}
|
||||
return host.querySelector(`[${BODY_DATA_ATTR}="true"]`);
|
||||
}
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentTextComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
||||
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: ContentTextComponent, isStandalone: true, selector: "content-text", inputs: { className: "className", weight: "weight", text: "text", ellipsis: "ellipsis", blur: "blur", style: "style", onView: "onView", renderMention: "renderMention", renderTag: "renderTag", renderLink: "renderLink" }, outputs: { viewed: "viewed", expandedChange: "expandedChange" }, viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
|
||||
<div #container>
|
||||
<tach-typography
|
||||
as="p"
|
||||
variant="Body"
|
||||
[weight]="weight"
|
||||
[className]="className"
|
||||
[preserveStyle]="mergedStyle"
|
||||
[ellipsis]="rowsEllipsis"
|
||||
[hostProps]="paragraphHostProps"
|
||||
>
|
||||
<ng-container *ngFor="let part of visibleParts; trackBy: trackByKey">
|
||||
<ng-container [ngSwitch]="part.kind">
|
||||
<ng-container *ngSwitchCase="'text'">{{ part.text }}</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'mention'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultMention">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultMention>
|
||||
<tach-typography as="span" variant="Body" color="link" [weight]="weight">
|
||||
{{ part.entity.displayText }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'tag'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultTag">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultTag>
|
||||
<tach-typography as="span" variant="Body" color="link" [weight]="weight">
|
||||
{{ part.entity.text }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'link'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultLink">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultLink>
|
||||
<tach-typography
|
||||
as="a"
|
||||
variant="Body"
|
||||
color="link"
|
||||
[weight]="weight"
|
||||
[hostProps]="buildDefaultLinkHostProps(part.entity.url)"
|
||||
>
|
||||
{{ part.entity.text }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchDefault></ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="showTrailingEllipsis">…</ng-container>
|
||||
|
||||
<button
|
||||
*ngIf="showInlineReadMore"
|
||||
type="button"
|
||||
(click)="handleExpand($event)"
|
||||
style="border: 0; background: none; padding: 0; margin-left: 6px; cursor: pointer;"
|
||||
>
|
||||
<tach-typography as="span" variant="Body" color="link" weight="bold">
|
||||
{{ readMoreLabel }}
|
||||
</tach-typography>
|
||||
</button>
|
||||
</tach-typography>
|
||||
|
||||
<button
|
||||
*ngIf="showBlockReadMore"
|
||||
type="button"
|
||||
(click)="handleExpand($event)"
|
||||
style="border: 0; background: none; padding: 0; margin-top: 6px; cursor: pointer;"
|
||||
>
|
||||
<tach-typography as="span" variant="Body" color="link" weight="bold">
|
||||
{{ readMoreLabel }}
|
||||
</tach-typography>
|
||||
</button>
|
||||
</div>
|
||||
`, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: TachTypographyComponent, selector: "tach-typography", inputs: ["as", "variant", "color", "weight", "clickable", "markdownEnabled", "markdown", "className", "ellipsis", "nzProps", "hostProps", "preserveStyle"], outputs: ["tachClick"] }, { kind: "directive", type: ContentSuggestionsRenderResultDirective, selector: "[contentSuggestionsRenderResult]", inputs: ["contentSuggestionsRenderResult"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentTextComponent, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
selector: "content-text",
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgFor,
|
||||
NgIf,
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
NgSwitchDefault,
|
||||
TachTypographyComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<div #container>
|
||||
<tach-typography
|
||||
as="p"
|
||||
variant="Body"
|
||||
[weight]="weight"
|
||||
[className]="className"
|
||||
[preserveStyle]="mergedStyle"
|
||||
[ellipsis]="rowsEllipsis"
|
||||
[hostProps]="paragraphHostProps"
|
||||
>
|
||||
<ng-container *ngFor="let part of visibleParts; trackBy: trackByKey">
|
||||
<ng-container [ngSwitch]="part.kind">
|
||||
<ng-container *ngSwitchCase="'text'">{{ part.text }}</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'mention'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultMention">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultMention>
|
||||
<tach-typography as="span" variant="Body" color="link" [weight]="weight">
|
||||
{{ part.entity.displayText }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'tag'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultTag">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultTag>
|
||||
<tach-typography as="span" variant="Body" color="link" [weight]="weight">
|
||||
{{ part.entity.text }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'link'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultLink">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultLink>
|
||||
<tach-typography
|
||||
as="a"
|
||||
variant="Body"
|
||||
color="link"
|
||||
[weight]="weight"
|
||||
[hostProps]="buildDefaultLinkHostProps(part.entity.url)"
|
||||
>
|
||||
{{ part.entity.text }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchDefault></ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="showTrailingEllipsis">…</ng-container>
|
||||
|
||||
<button
|
||||
*ngIf="showInlineReadMore"
|
||||
type="button"
|
||||
(click)="handleExpand($event)"
|
||||
style="border: 0; background: none; padding: 0; margin-left: 6px; cursor: pointer;"
|
||||
>
|
||||
<tach-typography as="span" variant="Body" color="link" weight="bold">
|
||||
{{ readMoreLabel }}
|
||||
</tach-typography>
|
||||
</button>
|
||||
</tach-typography>
|
||||
|
||||
<button
|
||||
*ngIf="showBlockReadMore"
|
||||
type="button"
|
||||
(click)="handleExpand($event)"
|
||||
style="border: 0; background: none; padding: 0; margin-top: 6px; cursor: pointer;"
|
||||
>
|
||||
<tach-typography as="span" variant="Body" color="link" weight="bold">
|
||||
{{ readMoreLabel }}
|
||||
</tach-typography>
|
||||
</button>
|
||||
</div>
|
||||
`,
|
||||
}]
|
||||
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { className: [{
|
||||
type: Input
|
||||
}], weight: [{
|
||||
type: Input
|
||||
}], text: [{
|
||||
type: Input
|
||||
}], ellipsis: [{
|
||||
type: Input
|
||||
}], blur: [{
|
||||
type: Input
|
||||
}], style: [{
|
||||
type: Input
|
||||
}], onView: [{
|
||||
type: Input
|
||||
}], renderMention: [{
|
||||
type: Input
|
||||
}], renderTag: [{
|
||||
type: Input
|
||||
}], renderLink: [{
|
||||
type: Input
|
||||
}], viewed: [{
|
||||
type: Output
|
||||
}], expandedChange: [{
|
||||
type: Output
|
||||
}], containerRef: [{
|
||||
type: ViewChild,
|
||||
args: ["container", { static: true }]
|
||||
}] } });
|
||||
//# sourceMappingURL=content-text.component.js.map
|
||||
1
packages/content-suggestions/dist/angular/angular/content-text.component.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/content-text.component.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
20
packages/content-suggestions/dist/angular/angular/content-title-with-suggestions.component.d.ts
vendored
Normal file
20
packages/content-suggestions/dist/angular/angular/content-title-with-suggestions.component.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { AngularContentEllipsisConfig, AngularLinkRenderer, AngularMentionRenderer, AngularTagRenderer } from "./types";
|
||||
import { EventEmitter } from "@angular/core";
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class ContentTitleWithSuggestionsComponent {
|
||||
className: string | undefined;
|
||||
text: string | null | undefined;
|
||||
ellipsis: AngularContentEllipsisConfig | undefined;
|
||||
blur: boolean;
|
||||
style: Record<string, string | number> | null | undefined;
|
||||
onView: (() => void) | undefined;
|
||||
renderMention: AngularMentionRenderer | undefined;
|
||||
renderTag: AngularTagRenderer | undefined;
|
||||
renderLink: AngularLinkRenderer | undefined;
|
||||
readonly viewed: EventEmitter<void>;
|
||||
readonly expandedChange: EventEmitter<boolean>;
|
||||
get normalizedEllipsis(): AngularContentEllipsisConfig;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<ContentTitleWithSuggestionsComponent, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<ContentTitleWithSuggestionsComponent, "content-title-with-suggestions", never, { "className": { "alias": "className"; "required": false; }; "text": { "alias": "text"; "required": false; }; "ellipsis": { "alias": "ellipsis"; "required": false; }; "blur": { "alias": "blur"; "required": false; }; "style": { "alias": "style"; "required": false; }; "onView": { "alias": "onView"; "required": false; }; "renderMention": { "alias": "renderMention"; "required": false; }; "renderTag": { "alias": "renderTag"; "required": false; }; "renderLink": { "alias": "renderLink"; "required": false; }; }, { "viewed": "viewed"; "expandedChange": "expandedChange"; }, never, never, true, never>;
|
||||
}
|
||||
//# sourceMappingURL=content-title-with-suggestions.component.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"content-title-with-suggestions.component.d.ts","sourceRoot":"","sources":["../../../src/angular/content-title-with-suggestions.component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,4BAA4B,EAC5B,mBAAmB,EACnB,sBAAsB,EAEtB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAsC,YAAY,EAAiB,MAAM,eAAe,CAAC;;AAIhG,qBAsBa,oCAAoC;IACtC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,4BAA4B,GAAG,SAAS,CAAC;IACnD,IAAI,UAAS;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1D,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAClD,SAAS,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC1C,UAAU,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAE3C,QAAQ,CAAC,MAAM,qBAA4B;IAC3C,QAAQ,CAAC,cAAc,wBAA+B;IAEhE,IAAI,kBAAkB,IAAI,4BAA4B,CAMrD;yCApBU,oCAAoC;2CAApC,oCAAoC;CAqBhD"}
|
||||
87
packages/content-suggestions/dist/angular/angular/content-title-with-suggestions.component.js
vendored
Normal file
87
packages/content-suggestions/dist/angular/angular/content-title-with-suggestions.component.js
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
import { ContentTextWithSuggestionsComponent } from "./content-text-with-suggestions.component";
|
||||
import * as i0 from "@angular/core";
|
||||
export class ContentTitleWithSuggestionsComponent {
|
||||
className;
|
||||
text;
|
||||
ellipsis;
|
||||
blur = false;
|
||||
style;
|
||||
onView;
|
||||
renderMention;
|
||||
renderTag;
|
||||
renderLink;
|
||||
viewed = new EventEmitter();
|
||||
expandedChange = new EventEmitter();
|
||||
get normalizedEllipsis() {
|
||||
if (this.ellipsis !== undefined) {
|
||||
return this.ellipsis;
|
||||
}
|
||||
return { rows: 2 };
|
||||
}
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentTitleWithSuggestionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: ContentTitleWithSuggestionsComponent, isStandalone: true, selector: "content-title-with-suggestions", inputs: { className: "className", text: "text", ellipsis: "ellipsis", blur: "blur", style: "style", onView: "onView", renderMention: "renderMention", renderTag: "renderTag", renderLink: "renderLink" }, outputs: { viewed: "viewed", expandedChange: "expandedChange" }, ngImport: i0, template: `
|
||||
<content-text-with-suggestions
|
||||
[className]="className"
|
||||
weight="bold"
|
||||
[text]="text"
|
||||
[ellipsis]="normalizedEllipsis"
|
||||
[blur]="blur"
|
||||
[style]="style"
|
||||
[onView]="onView"
|
||||
[renderMention]="renderMention"
|
||||
[renderTag]="renderTag"
|
||||
[renderLink]="renderLink"
|
||||
(viewed)="viewed.emit()"
|
||||
(expandedChange)="expandedChange.emit($event)"
|
||||
></content-text-with-suggestions>
|
||||
`, 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)"
|
||||
></content-text-with-suggestions>
|
||||
`,
|
||||
}]
|
||||
}], propDecorators: { className: [{
|
||||
type: Input
|
||||
}], text: [{
|
||||
type: Input
|
||||
}], ellipsis: [{
|
||||
type: Input
|
||||
}], blur: [{
|
||||
type: Input
|
||||
}], style: [{
|
||||
type: Input
|
||||
}], onView: [{
|
||||
type: Input
|
||||
}], renderMention: [{
|
||||
type: Input
|
||||
}], renderTag: [{
|
||||
type: Input
|
||||
}], renderLink: [{
|
||||
type: Input
|
||||
}], viewed: [{
|
||||
type: Output
|
||||
}], expandedChange: [{
|
||||
type: Output
|
||||
}] } });
|
||||
//# sourceMappingURL=content-title-with-suggestions.component.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"content-title-with-suggestions.component.js","sourceRoot":"","sources":["../../../src/angular/content-title-with-suggestions.component.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEhG,OAAO,EAAE,mCAAmC,EAAE,MAAM,2CAA2C,CAAC;;AAwBhG,MAAM,OAAO,oCAAoC;IACtC,SAAS,CAAqB;IAC9B,IAAI,CAA4B;IAChC,QAAQ,CAA2C;IACnD,IAAI,GAAG,KAAK,CAAC;IACb,KAAK,CAAqD;IAC1D,MAAM,CAA2B;IACjC,aAAa,CAAqC;IAClD,SAAS,CAAiC;IAC1C,UAAU,CAAkC;IAElC,MAAM,GAAG,IAAI,YAAY,EAAQ,CAAC;IAClC,cAAc,GAAG,IAAI,YAAY,EAAW,CAAC;IAEhE,IAAI,kBAAkB;QACpB,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC,QAAQ,CAAC;QACvB,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,CAAC,EAAsC,CAAC;IACzD,CAAC;wGApBU,oCAAoC;4FAApC,oCAAoC,qWAjBrC;;;;;;;;;;;;;;;GAeT,4DAjBS,mCAAmC;;4FAmBlC,oCAAoC;kBAtBhD,SAAS;mBAAC;oBACT,QAAQ,EAAE,gCAAgC;oBAC1C,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,mCAAmC,CAAC;oBAC9C,eAAe,EAAE,uBAAuB,CAAC,MAAM;oBAC/C,QAAQ,EAAE;;;;;;;;;;;;;;;GAeT;iBACF;;sBAEE,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBAEL,MAAM;;sBACN,MAAM"}
|
||||
18
packages/content-suggestions/dist/angular/angular/index.d.ts
vendored
Normal file
18
packages/content-suggestions/dist/angular/angular/index.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import { ContentTextComponent } from "./content-text.component";
|
||||
import { ContentTextWithSuggestionsComponent } from "./content-text-with-suggestions.component";
|
||||
import { ContentTitleWithSuggestionsComponent } from "./content-title-with-suggestions.component";
|
||||
import { ContentSuggestionsRenderResultDirective } from "./render-result.directive";
|
||||
import * as i0 from "@angular/core";
|
||||
import * as i1 from "./content-text.component";
|
||||
import * as i2 from "./content-text-with-suggestions.component";
|
||||
import * as i3 from "./content-title-with-suggestions.component";
|
||||
import * as i4 from "./render-result.directive";
|
||||
export type { AngularContentEllipsisConfig, AngularContentSnapshot, AngularContentTextProps, AngularContentTextWithSuggestionsProps, AngularContentTitleWithSuggestionsProps, AngularContentToken, AngularCountEllipsisConfig, AngularEllipsisExpandHandler, AngularEllipsisSymbol, AngularExpandInfo, AngularLinkRenderer, AngularLinkToken, AngularMentionRenderer, AngularMentionToken, AngularRenderResult, AngularRowsEllipsisConfig, AngularTagRenderer, AngularTagToken, AngularTextToken, } from "./types";
|
||||
export { AngularContentSuggestionsAdapter, buildAngularTagHref, createAngularContentTokens, } from "./tokens";
|
||||
export { ContentTextComponent, ContentTextWithSuggestionsComponent, ContentTitleWithSuggestionsComponent, ContentSuggestionsRenderResultDirective, };
|
||||
export declare class ContentSuggestionsAngularModule {
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<ContentSuggestionsAngularModule, never>;
|
||||
static ɵmod: i0.ɵɵNgModuleDeclaration<ContentSuggestionsAngularModule, never, [typeof i1.ContentTextComponent, typeof i2.ContentTextWithSuggestionsComponent, typeof i3.ContentTitleWithSuggestionsComponent, typeof i4.ContentSuggestionsRenderResultDirective], [typeof i1.ContentTextComponent, typeof i2.ContentTextWithSuggestionsComponent, typeof i3.ContentTitleWithSuggestionsComponent, typeof i4.ContentSuggestionsRenderResultDirective]>;
|
||||
static ɵinj: i0.ɵɵInjectorDeclaration<ContentSuggestionsAngularModule>;
|
||||
}
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/angular/index.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/index.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/angular/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,mCAAmC,EAAE,MAAM,2CAA2C,CAAC;AAChG,OAAO,EAAE,oCAAoC,EAAE,MAAM,4CAA4C,CAAC;AAClG,OAAO,EAAE,uCAAuC,EAAE,MAAM,2BAA2B,CAAC;;;;;;AAEpF,YAAY,EACV,4BAA4B,EAC5B,sBAAsB,EACtB,uBAAuB,EACvB,sCAAsC,EACtC,uCAAuC,EACvC,mBAAmB,EACnB,0BAA0B,EAC1B,4BAA4B,EAC5B,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,yBAAyB,EACzB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,gCAAgC,EAChC,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,oBAAoB,EACpB,mCAAmC,EACnC,oCAAoC,EACpC,uCAAuC,GACxC,CAAC;AAEF,qBAca,+BAA+B;yCAA/B,+BAA+B;0CAA/B,+BAA+B;0CAA/B,+BAA+B;CAAG"}
|
||||
39
packages/content-suggestions/dist/angular/angular/index.js
vendored
Normal file
39
packages/content-suggestions/dist/angular/angular/index.js
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { ContentTextComponent } from "./content-text.component";
|
||||
import { ContentTextWithSuggestionsComponent } from "./content-text-with-suggestions.component";
|
||||
import { ContentTitleWithSuggestionsComponent } from "./content-title-with-suggestions.component";
|
||||
import { ContentSuggestionsRenderResultDirective } from "./render-result.directive";
|
||||
import * as i0 from "@angular/core";
|
||||
export { AngularContentSuggestionsAdapter, buildAngularTagHref, createAngularContentTokens, } from "./tokens";
|
||||
export { ContentTextComponent, ContentTextWithSuggestionsComponent, ContentTitleWithSuggestionsComponent, ContentSuggestionsRenderResultDirective, };
|
||||
export class ContentSuggestionsAngularModule {
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentSuggestionsAngularModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
||||
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.18", ngImport: i0, type: ContentSuggestionsAngularModule, imports: [ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective], exports: [ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective] });
|
||||
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentSuggestionsAngularModule, imports: [ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent] });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentSuggestionsAngularModule, decorators: [{
|
||||
type: NgModule,
|
||||
args: [{
|
||||
imports: [
|
||||
ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
],
|
||||
exports: [
|
||||
ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
],
|
||||
}]
|
||||
}] });
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/content-suggestions/dist/angular/angular/index.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/angular/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,mCAAmC,EAAE,MAAM,2CAA2C,CAAC;AAChG,OAAO,EAAE,oCAAoC,EAAE,MAAM,4CAA4C,CAAC;AAClG,OAAO,EAAE,uCAAuC,EAAE,MAAM,2BAA2B,CAAC;;AAwBpF,OAAO,EACL,gCAAgC,EAChC,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,oBAAoB,EACpB,mCAAmC,EACnC,oCAAoC,EACpC,uCAAuC,GACxC,CAAC;AAgBF,MAAM,OAAO,+BAA+B;wGAA/B,+BAA+B;yGAA/B,+BAA+B,YAZxC,oBAAoB;YACpB,mCAAmC;YACnC,oCAAoC;YACpC,uCAAuC,aAGvC,oBAAoB;YACpB,mCAAmC;YACnC,oCAAoC;YACpC,uCAAuC;yGAG9B,+BAA+B,YAZxC,oBAAoB;YACpB,mCAAmC;YACnC,oCAAoC;;4FAU3B,+BAA+B;kBAd3C,QAAQ;mBAAC;oBACR,OAAO,EAAE;wBACP,oBAAoB;wBACpB,mCAAmC;wBACnC,oCAAoC;wBACpC,uCAAuC;qBACxC;oBACD,OAAO,EAAE;wBACP,oBAAoB;wBACpB,mCAAmC;wBACnC,oCAAoC;wBACpC,uCAAuC;qBACxC;iBACF"}
|
||||
2
packages/content-suggestions/dist/angular/angular/public-api.d.ts
vendored
Normal file
2
packages/content-suggestions/dist/angular/angular/public-api.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./index";
|
||||
//# sourceMappingURL=public-api.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/angular/public-api.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/public-api.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../../../src/angular/public-api.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC"}
|
||||
2
packages/content-suggestions/dist/angular/angular/public-api.js
vendored
Normal file
2
packages/content-suggestions/dist/angular/angular/public-api.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./index";
|
||||
//# sourceMappingURL=public-api.js.map
|
||||
1
packages/content-suggestions/dist/angular/angular/public-api.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/public-api.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../../src/angular/public-api.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC"}
|
||||
14
packages/content-suggestions/dist/angular/angular/render-result.directive.d.ts
vendored
Normal file
14
packages/content-suggestions/dist/angular/angular/render-result.directive.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { AngularRenderResult } from "./types";
|
||||
import { ElementRef, OnChanges, Renderer2 } from "@angular/core";
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class ContentSuggestionsRenderResultDirective implements OnChanges {
|
||||
private readonly elementRef;
|
||||
private readonly renderer;
|
||||
contentSuggestionsRenderResult: AngularRenderResult;
|
||||
constructor(elementRef: ElementRef<HTMLElement>, renderer: Renderer2);
|
||||
ngOnChanges(): void;
|
||||
private clearHost;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<ContentSuggestionsRenderResultDirective, never>;
|
||||
static ɵdir: i0.ɵɵDirectiveDeclaration<ContentSuggestionsRenderResultDirective, "[contentSuggestionsRenderResult]", never, { "contentSuggestionsRenderResult": { "alias": "contentSuggestionsRenderResult"; "required": false; }; }, {}, never, never, true, never>;
|
||||
}
|
||||
//# sourceMappingURL=render-result.directive.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/angular/render-result.directive.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/render-result.directive.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"render-result.directive.d.ts","sourceRoot":"","sources":["../../../src/angular/render-result.directive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAa,UAAU,EAAS,SAAS,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;;AAEnF,qBAIa,uCAAwC,YAAW,SAAS;IAIrE,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJlB,8BAA8B,EAAE,mBAAmB,CAAC;gBAG1C,UAAU,EAAE,UAAU,CAAC,WAAW,CAAC,EACnC,QAAQ,EAAE,SAAS;IAKtC,WAAW,IAAI,IAAI;IAiBnB,OAAO,CAAC,SAAS;yCA3BN,uCAAuC;2CAAvC,uCAAuC;CAkCnD"}
|
||||
43
packages/content-suggestions/dist/angular/angular/render-result.directive.js
vendored
Normal file
43
packages/content-suggestions/dist/angular/angular/render-result.directive.js
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Directive, Input } from "@angular/core";
|
||||
import * as i0 from "@angular/core";
|
||||
export class ContentSuggestionsRenderResultDirective {
|
||||
elementRef;
|
||||
renderer;
|
||||
contentSuggestionsRenderResult;
|
||||
constructor(elementRef, renderer) {
|
||||
this.elementRef = elementRef;
|
||||
this.renderer = renderer;
|
||||
this.renderer.setStyle(this.elementRef.nativeElement, "display", "contents");
|
||||
}
|
||||
ngOnChanges() {
|
||||
this.clearHost();
|
||||
const value = this.contentSuggestionsRenderResult;
|
||||
if (value === null || value === undefined) {
|
||||
return;
|
||||
}
|
||||
if (value instanceof Node) {
|
||||
this.renderer.appendChild(this.elementRef.nativeElement, value);
|
||||
return;
|
||||
}
|
||||
const textNode = this.renderer.createText(String(value));
|
||||
this.renderer.appendChild(this.elementRef.nativeElement, textNode);
|
||||
}
|
||||
clearHost() {
|
||||
const host = this.elementRef.nativeElement;
|
||||
while (host.firstChild) {
|
||||
host.removeChild(host.firstChild);
|
||||
}
|
||||
}
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentSuggestionsRenderResultDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
|
||||
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.18", type: ContentSuggestionsRenderResultDirective, isStandalone: true, selector: "[contentSuggestionsRenderResult]", inputs: { contentSuggestionsRenderResult: "contentSuggestionsRenderResult" }, usesOnChanges: true, ngImport: i0 });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: ContentSuggestionsRenderResultDirective, decorators: [{
|
||||
type: Directive,
|
||||
args: [{
|
||||
selector: "[contentSuggestionsRenderResult]",
|
||||
standalone: true,
|
||||
}]
|
||||
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { contentSuggestionsRenderResult: [{
|
||||
type: Input
|
||||
}] } });
|
||||
//# sourceMappingURL=render-result.directive.js.map
|
||||
1
packages/content-suggestions/dist/angular/angular/render-result.directive.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/render-result.directive.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"render-result.directive.js","sourceRoot":"","sources":["../../../src/angular/render-result.directive.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAc,KAAK,EAAwB,MAAM,eAAe,CAAC;;AAMnF,MAAM,OAAO,uCAAuC;IAI/B;IACA;IAJV,8BAA8B,CAAsB;IAE7D,YACmB,UAAmC,EACnC,QAAmB;QADnB,eAAU,GAAV,UAAU,CAAyB;QACnC,aAAQ,GAAR,QAAQ,CAAW;QAEpC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAC/E,CAAC;IAED,WAAW;QACT,IAAI,CAAC,SAAS,EAAE,CAAC;QAEjB,MAAM,KAAK,GAAG,IAAI,CAAC,8BAA8B,CAAC;QAClD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC1C,OAAO;QACT,CAAC;QAED,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;YAC1B,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;YAChE,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IACrE,CAAC;IAEO,SAAS;QACf,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;QAE3C,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC;YACvB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;wGAjCU,uCAAuC;4FAAvC,uCAAuC;;4FAAvC,uCAAuC;kBAJnD,SAAS;mBAAC;oBACT,QAAQ,EAAE,kCAAkC;oBAC5C,UAAU,EAAE,IAAI;iBACjB;;sBAEE,KAAK"}
|
||||
8
packages/content-suggestions/dist/angular/angular/tokens.d.ts
vendored
Normal file
8
packages/content-suggestions/dist/angular/angular/tokens.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { AngularContentSnapshot, AngularContentToken } from "./types";
|
||||
import type { TagEntity } from "../core";
|
||||
export declare const createAngularContentTokens: (inputText: string | null | undefined) => AngularContentToken[];
|
||||
export declare const buildAngularTagHref: (entity: TagEntity) => string;
|
||||
export declare class AngularContentSuggestionsAdapter {
|
||||
snapshot(inputText: string | null | undefined): AngularContentSnapshot;
|
||||
}
|
||||
//# sourceMappingURL=tokens.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/angular/tokens.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/tokens.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../src/angular/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,mBAAmB,EAKpB,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,SAAS,CAAC;AAgCxD,eAAO,MAAM,0BAA0B,GACrC,WAAW,MAAM,GAAG,IAAI,GAAG,SAAS,KACnC,mBAAmB,EAqBrB,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,QAAQ,SAAS,KAAG,MAEvD,CAAC;AAEF,qBAAa,gCAAgC;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,sBAAsB;CAWvE"}
|
||||
58
packages/content-suggestions/dist/angular/angular/tokens.js
vendored
Normal file
58
packages/content-suggestions/dist/angular/angular/tokens.js
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
import { findAllEntities } from "../core";
|
||||
const mapEntityToToken = (entity) => {
|
||||
if (entity.type === "mention") {
|
||||
return {
|
||||
kind: "mention",
|
||||
entity,
|
||||
};
|
||||
}
|
||||
if (entity.type === "tag") {
|
||||
return {
|
||||
kind: "tag",
|
||||
entity,
|
||||
};
|
||||
}
|
||||
return {
|
||||
kind: "link",
|
||||
entity,
|
||||
};
|
||||
};
|
||||
const createTextToken = (text, start, end) => ({
|
||||
kind: "text",
|
||||
text,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
export const createAngularContentTokens = (inputText) => {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
let cursor = 0;
|
||||
const tokens = [];
|
||||
for (const entity of entities) {
|
||||
if (entity.start > cursor) {
|
||||
tokens.push(createTextToken(text.slice(cursor, entity.start), cursor, entity.start));
|
||||
}
|
||||
tokens.push(mapEntityToToken(entity));
|
||||
cursor = entity.end;
|
||||
}
|
||||
if (cursor < text.length) {
|
||||
tokens.push(createTextToken(text.slice(cursor), cursor, text.length));
|
||||
}
|
||||
return tokens;
|
||||
};
|
||||
export const buildAngularTagHref = (entity) => {
|
||||
return `/search/?query=${encodeURIComponent(entity.tag.toLowerCase())}`;
|
||||
};
|
||||
export class AngularContentSuggestionsAdapter {
|
||||
snapshot(inputText) {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
const tokens = createAngularContentTokens(text);
|
||||
return {
|
||||
text,
|
||||
entities,
|
||||
tokens,
|
||||
};
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=tokens.js.map
|
||||
1
packages/content-suggestions/dist/angular/angular/tokens.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/tokens.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../../src/angular/tokens.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,gBAAgB,GAAG,CAAC,MAAqB,EAA4D,EAAE;IAC3G,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO;YACL,IAAI,EAAE,SAAS;YACf,MAAM;SACP,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO;YACL,IAAI,EAAE,KAAK;YACX,MAAM;SACP,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,MAAM;KACP,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,IAAY,EAAE,KAAa,EAAE,GAAW,EAAoB,EAAE,CAAC,CAAC;IACvF,IAAI,EAAE,MAAM;IACZ,IAAI;IACJ,KAAK;IACL,GAAG;CACJ,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,SAAoC,EACb,EAAE;IACzB,MAAM,IAAI,GAAG,SAAS,IAAI,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IAEvC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,MAAM,GAA0B,EAAE,CAAC;IAEzC,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC9B,IAAI,MAAM,CAAC,KAAK,GAAG,MAAM,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACvF,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;QACtC,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC;IACtB,CAAC;IAED,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAiB,EAAU,EAAE;IAC/D,OAAO,kBAAkB,kBAAkB,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;AAC1E,CAAC,CAAC;AAEF,MAAM,OAAO,gCAAgC;IAC3C,QAAQ,CAAC,SAAoC;QAC3C,MAAM,IAAI,GAAG,SAAS,IAAI,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,0BAA0B,CAAC,IAAI,CAAC,CAAC;QAEhD,OAAO;YACL,IAAI;YACJ,QAAQ;YACR,MAAM;SACP,CAAC;IACJ,CAAC;CACF"}
|
||||
70
packages/content-suggestions/dist/angular/angular/types.d.ts
vendored
Normal file
70
packages/content-suggestions/dist/angular/angular/types.d.ts
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
import type { ContentEntity, LinkEntity, MentionEntity, TagEntity } from "../core";
|
||||
import type { TypographyWeight } from "@hublib-web/tach-typography/core";
|
||||
export interface AngularTextToken {
|
||||
kind: "text";
|
||||
text: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
export interface AngularMentionToken {
|
||||
kind: "mention";
|
||||
entity: MentionEntity;
|
||||
}
|
||||
export interface AngularTagToken {
|
||||
kind: "tag";
|
||||
entity: TagEntity;
|
||||
}
|
||||
export interface AngularLinkToken {
|
||||
kind: "link";
|
||||
entity: LinkEntity;
|
||||
}
|
||||
export type AngularContentToken = AngularTextToken | AngularMentionToken | AngularTagToken | AngularLinkToken;
|
||||
export interface AngularContentSnapshot {
|
||||
text: string;
|
||||
entities: ContentEntity[];
|
||||
tokens: AngularContentToken[];
|
||||
}
|
||||
export interface AngularExpandInfo {
|
||||
expanded: boolean;
|
||||
}
|
||||
export type AngularEllipsisSymbol = string | ((expanded: boolean) => string);
|
||||
export type AngularEllipsisExpandHandler = ((expanded: boolean) => void) | ((event: MouseEvent, info: AngularExpandInfo) => void);
|
||||
export type AngularCountEllipsisConfig = {
|
||||
count: number;
|
||||
rows?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: AngularEllipsisExpandHandler;
|
||||
};
|
||||
export type AngularRowsEllipsisConfig = {
|
||||
rows: number;
|
||||
count?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: AngularEllipsisExpandHandler;
|
||||
};
|
||||
export type AngularContentEllipsisConfig = AngularCountEllipsisConfig | AngularRowsEllipsisConfig | false;
|
||||
export type AngularRenderResult = Node | string | number | null | undefined;
|
||||
export type AngularMentionRenderer = (entity: MentionEntity, index: number) => AngularRenderResult;
|
||||
export type AngularTagRenderer = (entity: TagEntity, index: number) => AngularRenderResult;
|
||||
export type AngularLinkRenderer = (entity: LinkEntity, index: number) => AngularRenderResult;
|
||||
export interface AngularContentTextProps {
|
||||
className?: string;
|
||||
weight?: TypographyWeight;
|
||||
text?: string | null;
|
||||
ellipsis?: AngularContentEllipsisConfig;
|
||||
blur?: boolean;
|
||||
style?: Record<string, string | number> | null;
|
||||
onView?: () => void;
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
renderLink?: AngularLinkRenderer;
|
||||
}
|
||||
export type AngularContentTextWithSuggestionsProps = Omit<AngularContentTextProps, "renderMention" | "renderTag"> & {
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
};
|
||||
export type AngularContentTitleWithSuggestionsProps = Omit<AngularContentTextWithSuggestionsProps, "weight">;
|
||||
//# sourceMappingURL=types.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/angular/types.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/types.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/angular/types.ts"],"names":[],"mappings":"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"}
|
||||
2
packages/content-suggestions/dist/angular/angular/types.js
vendored
Normal file
2
packages/content-suggestions/dist/angular/angular/types.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=types.js.map
|
||||
1
packages/content-suggestions/dist/angular/angular/types.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/angular/types.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/angular/types.ts"],"names":[],"mappings":""}
|
||||
3
packages/content-suggestions/dist/angular/core/index.d.ts
vendored
Normal file
3
packages/content-suggestions/dist/angular/core/index.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export type { BaseEntity, ContentEntity, LinkEntity, MentionEntity, ParsedMention, ProcessedContent, TagEntity, } from "./types";
|
||||
export { findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent, } from "./parser";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/core/index.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/core/index.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,UAAU,EACV,aAAa,EACb,UAAU,EACV,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,eAAe,EACf,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,cAAc,GACf,MAAM,UAAU,CAAC"}
|
||||
2
packages/content-suggestions/dist/angular/core/index.js
vendored
Normal file
2
packages/content-suggestions/dist/angular/core/index.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export { findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent, } from "./parser";
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/content-suggestions/dist/angular/core/index.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/core/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,eAAe,EACf,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,cAAc,GACf,MAAM,UAAU,CAAC"}
|
||||
9
packages/content-suggestions/dist/angular/core/parser.d.ts
vendored
Normal file
9
packages/content-suggestions/dist/angular/core/parser.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { ContentEntity, LinkEntity, MentionEntity, ParsedMention, ProcessedContent, TagEntity } from "./types";
|
||||
export declare const mentionLinkRegexp: RegExp;
|
||||
export declare const parseMention: (mention: string) => ParsedMention | null;
|
||||
export declare const findMentions: (text: string) => MentionEntity[];
|
||||
export declare const findTags: (content: string) => TagEntity[];
|
||||
export declare const findLinks: (content: string) => LinkEntity[];
|
||||
export declare const findAllEntities: (content: string) => ContentEntity[];
|
||||
export declare const processContent: (content: string) => ProcessedContent;
|
||||
//# sourceMappingURL=parser.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/core/parser.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/core/parser.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/core/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,UAAU,EACV,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,SAAS,EACV,MAAM,SAAS,CAAC;AAEjB,eAAO,MAAM,iBAAiB,QACgE,CAAC;AAE/F,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,KAAG,aAAa,GAAG,IAkB9D,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,aAAa,EAkBxD,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,SAAS,MAAM,KAAG,SAAS,EAiBnD,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,SAAS,MAAM,KAAG,UAAU,EAqBrD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,KAAG,aAAa,EAM9D,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,gBAYhD,CAAC"}
|
||||
85
packages/content-suggestions/dist/angular/core/parser.js
vendored
Normal file
85
packages/content-suggestions/dist/angular/core/parser.js
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
export const mentionLinkRegexp = /@\[[^\]]+]\([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\)/g;
|
||||
export const parseMention = (mention) => {
|
||||
const regex = /@\[([^\]]+)\]\(([\w-]{36})\)/;
|
||||
const match = mention.match(regex);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
const mentionText = match[1];
|
||||
const mentionId = match[2];
|
||||
if (!mentionText || !mentionId) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
mention: `@${mentionText}`,
|
||||
id: mentionId,
|
||||
};
|
||||
};
|
||||
export const findMentions = (text) => {
|
||||
let match;
|
||||
const matches = [];
|
||||
while ((match = mentionLinkRegexp.exec(text)) !== null) {
|
||||
const parsed = parseMention(match[0]);
|
||||
const baseMention = {
|
||||
start: match.index,
|
||||
end: mentionLinkRegexp.lastIndex,
|
||||
text: match[0],
|
||||
type: "mention",
|
||||
displayText: parsed?.mention ?? match[0],
|
||||
};
|
||||
matches.push(parsed?.id ? { ...baseMention, userId: parsed.id } : baseMention);
|
||||
}
|
||||
return matches;
|
||||
};
|
||||
export const findTags = (content) => {
|
||||
const regex = /#[^\s]{1,201}/g;
|
||||
const results = [];
|
||||
let match;
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
const value = match[0];
|
||||
results.push({
|
||||
start: match.index,
|
||||
end: match.index + value.length,
|
||||
text: value,
|
||||
type: "tag",
|
||||
tag: value.replace("#", ""),
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
export const findLinks = (content) => {
|
||||
const regex = /\b((https?:\/\/)?(?:[\w-]+\.)+[a-z]{2,}(\/[\w\-._~:/?#[\]@!$&'()*+,;=]*)?)/gi;
|
||||
const results = [];
|
||||
let match;
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
const rawUrl = match[0];
|
||||
const hasProtocol = /^https?:\/\//i.test(rawUrl);
|
||||
const fullUrl = hasProtocol ? rawUrl : `https://${rawUrl}`;
|
||||
results.push({
|
||||
start: match.index,
|
||||
end: match.index + rawUrl.length,
|
||||
text: rawUrl,
|
||||
url: fullUrl,
|
||||
type: "link",
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
export const findAllEntities = (content) => {
|
||||
const mentions = findMentions(content);
|
||||
const tags = findTags(content);
|
||||
const links = findLinks(content);
|
||||
return [...mentions, ...tags, ...links].sort((a, b) => a.start - b.start);
|
||||
};
|
||||
export const processContent = (content) => {
|
||||
const processedText = content.replace(mentionLinkRegexp, match => {
|
||||
const parsed = parseMention(match);
|
||||
return parsed ? parsed.mention : match;
|
||||
});
|
||||
const tags = findTags(content).map(tag => tag.tag);
|
||||
return {
|
||||
processedText,
|
||||
tags,
|
||||
};
|
||||
};
|
||||
//# sourceMappingURL=parser.js.map
|
||||
1
packages/content-suggestions/dist/angular/core/parser.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/core/parser.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../src/core/parser.ts"],"names":[],"mappings":"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"}
|
||||
28
packages/content-suggestions/dist/angular/core/types.d.ts
vendored
Normal file
28
packages/content-suggestions/dist/angular/core/types.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
export interface BaseEntity {
|
||||
start: number;
|
||||
end: number;
|
||||
text: string;
|
||||
}
|
||||
export interface MentionEntity extends BaseEntity {
|
||||
type: "mention";
|
||||
displayText: string;
|
||||
userId?: string;
|
||||
}
|
||||
export interface TagEntity extends BaseEntity {
|
||||
type: "tag";
|
||||
tag: string;
|
||||
}
|
||||
export interface LinkEntity extends BaseEntity {
|
||||
type: "link";
|
||||
url: string;
|
||||
}
|
||||
export type ContentEntity = MentionEntity | TagEntity | LinkEntity;
|
||||
export interface ParsedMention {
|
||||
mention: string;
|
||||
id: string;
|
||||
}
|
||||
export interface ProcessedContent {
|
||||
processedText: string;
|
||||
tags: string[];
|
||||
}
|
||||
//# sourceMappingURL=types.d.ts.map
|
||||
1
packages/content-suggestions/dist/angular/core/types.d.ts.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/core/types.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAU,SAAQ,UAAU;IAC3C,IAAI,EAAE,KAAK,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAW,SAAQ,UAAU;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,SAAS,GAAG,UAAU,CAAC;AAEnE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB"}
|
||||
2
packages/content-suggestions/dist/angular/core/types.js
vendored
Normal file
2
packages/content-suggestions/dist/angular/core/types.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=types.js.map
|
||||
1
packages/content-suggestions/dist/angular/core/types.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/core/types.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/types.ts"],"names":[],"mappings":""}
|
||||
141
packages/content-suggestions/dist/angular/index.cjs
vendored
141
packages/content-suggestions/dist/angular/index.cjs
vendored
@@ -1,141 +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 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
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
exports.AngularContentSuggestionsAdapter = AngularContentSuggestionsAdapter;
|
||||
exports.buildAngularTagHref = buildAngularTagHref;
|
||||
exports.createAngularContentTokens = createAngularContentTokens;
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,33 +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[];
|
||||
}
|
||||
declare const buildAngularTagHref: (entity: TagEntity) => string;
|
||||
declare const createAngularContentTokens: (inputText: string | null | undefined) => AngularContentToken[];
|
||||
declare class AngularContentSuggestionsAdapter {
|
||||
snapshot(inputText: string | null | undefined): AngularContentSnapshot;
|
||||
}
|
||||
|
||||
export { type AngularContentSnapshot, AngularContentSuggestionsAdapter, type AngularContentToken, type AngularLinkToken, type AngularMentionToken, type AngularTagToken, type AngularTextToken, buildAngularTagHref, createAngularContentTokens };
|
||||
@@ -1,33 +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[];
|
||||
}
|
||||
declare const buildAngularTagHref: (entity: TagEntity) => string;
|
||||
declare const createAngularContentTokens: (inputText: string | null | undefined) => AngularContentToken[];
|
||||
declare class AngularContentSuggestionsAdapter {
|
||||
snapshot(inputText: string | null | undefined): AngularContentSnapshot;
|
||||
}
|
||||
|
||||
export { type AngularContentSnapshot, AngularContentSuggestionsAdapter, type AngularContentToken, type AngularLinkToken, type AngularMentionToken, type AngularTagToken, type AngularTextToken, buildAngularTagHref, createAngularContentTokens };
|
||||
export * from "./angular/index";
|
||||
|
||||
138
packages/content-suggestions/dist/angular/index.js
vendored
138
packages/content-suggestions/dist/angular/index.js
vendored
@@ -1,137 +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 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
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export { AngularContentSuggestionsAdapter, buildAngularTagHref, createAngularContentTokens };
|
||||
//# sourceMappingURL=index.js.map
|
||||
//# sourceMappingURL=index.js.map
|
||||
export * from "./angular/index.js";
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,30 @@
|
||||
import { C as ContentEntity, L as LinkEntity, M as MentionEntity, T as TagEntity, P as ParsedMention, a as ProcessedContent } from '../types-BRt4hd7A.cjs';
|
||||
export { B as BaseEntity } from '../types-BRt4hd7A.cjs';
|
||||
interface BaseEntity {
|
||||
start: number;
|
||||
end: number;
|
||||
text: string;
|
||||
}
|
||||
interface MentionEntity extends BaseEntity {
|
||||
type: "mention";
|
||||
displayText: string;
|
||||
userId?: string;
|
||||
}
|
||||
interface TagEntity extends BaseEntity {
|
||||
type: "tag";
|
||||
tag: string;
|
||||
}
|
||||
interface LinkEntity extends BaseEntity {
|
||||
type: "link";
|
||||
url: string;
|
||||
}
|
||||
type ContentEntity = MentionEntity | TagEntity | LinkEntity;
|
||||
interface ParsedMention {
|
||||
mention: string;
|
||||
id: string;
|
||||
}
|
||||
interface ProcessedContent {
|
||||
processedText: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
declare const mentionLinkRegexp: RegExp;
|
||||
declare const parseMention: (mention: string) => ParsedMention | null;
|
||||
@@ -9,4 +34,4 @@ declare const findLinks: (content: string) => LinkEntity[];
|
||||
declare const findAllEntities: (content: string) => ContentEntity[];
|
||||
declare const processContent: (content: string) => ProcessedContent;
|
||||
|
||||
export { ContentEntity, LinkEntity, MentionEntity, ParsedMention, ProcessedContent, TagEntity, findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent };
|
||||
export { type BaseEntity, type ContentEntity, type LinkEntity, type MentionEntity, type ParsedMention, type ProcessedContent, type TagEntity, findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent };
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
import { C as ContentEntity, L as LinkEntity, M as MentionEntity, T as TagEntity, P as ParsedMention, a as ProcessedContent } from '../types-BRt4hd7A.js';
|
||||
export { B as BaseEntity } from '../types-BRt4hd7A.js';
|
||||
interface BaseEntity {
|
||||
start: number;
|
||||
end: number;
|
||||
text: string;
|
||||
}
|
||||
interface MentionEntity extends BaseEntity {
|
||||
type: "mention";
|
||||
displayText: string;
|
||||
userId?: string;
|
||||
}
|
||||
interface TagEntity extends BaseEntity {
|
||||
type: "tag";
|
||||
tag: string;
|
||||
}
|
||||
interface LinkEntity extends BaseEntity {
|
||||
type: "link";
|
||||
url: string;
|
||||
}
|
||||
type ContentEntity = MentionEntity | TagEntity | LinkEntity;
|
||||
interface ParsedMention {
|
||||
mention: string;
|
||||
id: string;
|
||||
}
|
||||
interface ProcessedContent {
|
||||
processedText: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
declare const mentionLinkRegexp: RegExp;
|
||||
declare const parseMention: (mention: string) => ParsedMention | null;
|
||||
@@ -9,4 +34,4 @@ declare const findLinks: (content: string) => LinkEntity[];
|
||||
declare const findAllEntities: (content: string) => ContentEntity[];
|
||||
declare const processContent: (content: string) => ProcessedContent;
|
||||
|
||||
export { ContentEntity, LinkEntity, MentionEntity, ParsedMention, ProcessedContent, TagEntity, findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent };
|
||||
export { type BaseEntity, type ContentEntity, type LinkEntity, type MentionEntity, type ParsedMention, type ProcessedContent, type TagEntity, findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent };
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,10 +1,9 @@
|
||||
import React, { ComponentProps, ReactNode } from 'react';
|
||||
import { EllipsisConfig } from 'antd/lib/typography/Base';
|
||||
import { M as MentionEntity, T as TagEntity, L as LinkEntity } from '../types-BRt4hd7A.cjs';
|
||||
export { B as BaseEntity, C as ContentEntity, P as ParsedMention, a as ProcessedContent } from '../types-BRt4hd7A.cjs';
|
||||
import { MentionEntity, TagEntity, LinkEntity } from '../core/index.cjs';
|
||||
export { BaseEntity, ContentEntity, ParsedMention, ProcessedContent, findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent } from '../core/index.cjs';
|
||||
import { TachTypography } from '@hublib-web/tach-typography/react';
|
||||
import * as react_jsx_runtime from 'react/jsx-runtime';
|
||||
export { findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent } from '../core/index.cjs';
|
||||
|
||||
type CustomEllipsisConfig = ({
|
||||
count: number;
|
||||
@@ -41,8 +40,7 @@ type BaseContentTextProps = Omit<ComponentProps<typeof ContentText>, "renderMent
|
||||
};
|
||||
declare const ContentTextWithSuggestions: ({ renderMention, renderTag, ...props }: BaseContentTextProps) => react_jsx_runtime.JSX.Element;
|
||||
|
||||
interface ContentTitleWithSuggestionsProps extends Omit<ComponentProps<typeof ContentTextWithSuggestions>, "weight"> {
|
||||
}
|
||||
type ContentTitleWithSuggestionsProps = Omit<ComponentProps<typeof ContentTextWithSuggestions>, "weight">;
|
||||
declare const ContentTitleWithSuggestions: ({ text, ellipsis, blur, ...rest }: ContentTitleWithSuggestionsProps) => react_jsx_runtime.JSX.Element;
|
||||
|
||||
export { ContentText, type ContentTextProps, ContentTextWithSuggestions, ContentTitleWithSuggestions, LinkEntity, MentionEntity, TagEntity };
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React, { ComponentProps, ReactNode } from 'react';
|
||||
import { EllipsisConfig } from 'antd/lib/typography/Base';
|
||||
import { M as MentionEntity, T as TagEntity, L as LinkEntity } from '../types-BRt4hd7A.js';
|
||||
export { B as BaseEntity, C as ContentEntity, P as ParsedMention, a as ProcessedContent } from '../types-BRt4hd7A.js';
|
||||
import { MentionEntity, TagEntity, LinkEntity } from '../core/index.js';
|
||||
export { BaseEntity, ContentEntity, ParsedMention, ProcessedContent, findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent } from '../core/index.js';
|
||||
import { TachTypography } from '@hublib-web/tach-typography/react';
|
||||
import * as react_jsx_runtime from 'react/jsx-runtime';
|
||||
export { findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent } from '../core/index.js';
|
||||
|
||||
type CustomEllipsisConfig = ({
|
||||
count: number;
|
||||
@@ -41,8 +40,7 @@ type BaseContentTextProps = Omit<ComponentProps<typeof ContentText>, "renderMent
|
||||
};
|
||||
declare const ContentTextWithSuggestions: ({ renderMention, renderTag, ...props }: BaseContentTextProps) => react_jsx_runtime.JSX.Element;
|
||||
|
||||
interface ContentTitleWithSuggestionsProps extends Omit<ComponentProps<typeof ContentTextWithSuggestions>, "weight"> {
|
||||
}
|
||||
type ContentTitleWithSuggestionsProps = Omit<ComponentProps<typeof ContentTextWithSuggestions>, "weight">;
|
||||
declare const ContentTitleWithSuggestions: ({ text, ellipsis, blur, ...rest }: ContentTitleWithSuggestionsProps) => react_jsx_runtime.JSX.Element;
|
||||
|
||||
export { ContentText, type ContentTextProps, ContentTextWithSuggestions, ContentTitleWithSuggestions, LinkEntity, MentionEntity, TagEntity };
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,29 +0,0 @@
|
||||
interface BaseEntity {
|
||||
start: number;
|
||||
end: number;
|
||||
text: string;
|
||||
}
|
||||
interface MentionEntity extends BaseEntity {
|
||||
type: "mention";
|
||||
displayText: string;
|
||||
userId?: string;
|
||||
}
|
||||
interface TagEntity extends BaseEntity {
|
||||
type: "tag";
|
||||
tag: string;
|
||||
}
|
||||
interface LinkEntity extends BaseEntity {
|
||||
type: "link";
|
||||
url: string;
|
||||
}
|
||||
type ContentEntity = MentionEntity | TagEntity | LinkEntity;
|
||||
interface ParsedMention {
|
||||
mention: string;
|
||||
id: string;
|
||||
}
|
||||
interface ProcessedContent {
|
||||
processedText: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export type { BaseEntity as B, ContentEntity as C, LinkEntity as L, MentionEntity as M, ParsedMention as P, TagEntity as T, ProcessedContent as a };
|
||||
@@ -1,29 +0,0 @@
|
||||
interface BaseEntity {
|
||||
start: number;
|
||||
end: number;
|
||||
text: string;
|
||||
}
|
||||
interface MentionEntity extends BaseEntity {
|
||||
type: "mention";
|
||||
displayText: string;
|
||||
userId?: string;
|
||||
}
|
||||
interface TagEntity extends BaseEntity {
|
||||
type: "tag";
|
||||
tag: string;
|
||||
}
|
||||
interface LinkEntity extends BaseEntity {
|
||||
type: "link";
|
||||
url: string;
|
||||
}
|
||||
type ContentEntity = MentionEntity | TagEntity | LinkEntity;
|
||||
interface ParsedMention {
|
||||
mention: string;
|
||||
id: string;
|
||||
}
|
||||
interface ProcessedContent {
|
||||
processedText: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export type { BaseEntity as B, ContentEntity as C, LinkEntity as L, MentionEntity as M, ParsedMention as P, TagEntity as T, ProcessedContent as a };
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hublib-web/content-suggestions",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.1",
|
||||
"description": "Content text/title with mentions, tags and links for React and Angular",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
@@ -43,15 +43,15 @@
|
||||
},
|
||||
"./angular": {
|
||||
"types": "./dist/angular/index.d.ts",
|
||||
"import": "./dist/angular/index.js",
|
||||
"require": "./dist/angular/index.cjs"
|
||||
"import": "./dist/angular/index.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn clean && tsup",
|
||||
"build": "yarn clean && tsup && yarn build:angular",
|
||||
"build:angular": "ngc -p tsconfig.angular.json && node ./scripts/fix-angular-entry.mjs",
|
||||
"clean": "rm -rf dist storybook-static",
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"test": "vitest run --passWithNoTests",
|
||||
"test": "yarn run -T vitest run --passWithNoTests",
|
||||
"lint": "eslint src --ext .ts,.tsx",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"storybook:build": "storybook build"
|
||||
@@ -59,7 +59,7 @@
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">=17.0.0",
|
||||
"@angular/core": ">=17.0.0",
|
||||
"@hublib-web/tach-typography": ">=0.1.0",
|
||||
"@hublib-web/tach-typography": "0.3.1",
|
||||
"antd": ">=5.0.0",
|
||||
"react": ">=18.0.0",
|
||||
"react-dom": ">=18.0.0",
|
||||
@@ -87,8 +87,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/common": "^20.3.17",
|
||||
"@angular/compiler": "^20.3.17",
|
||||
"@angular/compiler-cli": "^20.3.17",
|
||||
"@angular/core": "^20.3.17",
|
||||
"@hublib-web/tach-typography": "workspace:*",
|
||||
"@hublib-web/tach-typography": "workspace:0.3.1",
|
||||
"@storybook/addon-essentials": "8.6.14",
|
||||
"@storybook/react": "8.6.14",
|
||||
"@storybook/react-vite": "8.6.14",
|
||||
|
||||
12
packages/content-suggestions/scripts/fix-angular-entry.mjs
Normal file
12
packages/content-suggestions/scripts/fix-angular-entry.mjs
Normal file
@@ -0,0 +1,12 @@
|
||||
import { access, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
const distAngularDir = path.resolve("dist/angular");
|
||||
await access(path.join(distAngularDir, "angular/index.js"));
|
||||
await access(path.join(distAngularDir, "angular/index.d.ts"));
|
||||
|
||||
await writeFile(
|
||||
path.join(distAngularDir, "index.js"),
|
||||
'export * from "./angular/index.js";\n',
|
||||
);
|
||||
await writeFile(path.join(distAngularDir, "index.d.ts"), 'export * from "./angular/index";\n');
|
||||
@@ -0,0 +1,50 @@
|
||||
import type {
|
||||
AngularContentEllipsisConfig,
|
||||
AngularLinkRenderer,
|
||||
AngularMentionRenderer,
|
||||
AngularTagRenderer,
|
||||
} from "./types";
|
||||
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
|
||||
import type { TypographyWeight } from "@hublib-web/tach-typography/core";
|
||||
|
||||
import { ContentTextComponent } from "./content-text.component";
|
||||
|
||||
@Component({
|
||||
selector: "content-text-with-suggestions",
|
||||
standalone: true,
|
||||
imports: [ContentTextComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<content-text
|
||||
[className]="className"
|
||||
[weight]="weight"
|
||||
[text]="text"
|
||||
[ellipsis]="ellipsis"
|
||||
[blur]="blur"
|
||||
[style]="style"
|
||||
[onView]="onView"
|
||||
[renderMention]="renderMention"
|
||||
[renderTag]="renderTag"
|
||||
[renderLink]="renderLink"
|
||||
(viewed)="viewed.emit()"
|
||||
(expandedChange)="expandedChange.emit($event)"
|
||||
></content-text>
|
||||
`,
|
||||
})
|
||||
export class ContentTextWithSuggestionsComponent {
|
||||
@Input() className: string | undefined;
|
||||
@Input() weight: TypographyWeight = "normal";
|
||||
@Input() text: string | null | undefined;
|
||||
@Input() ellipsis: AngularContentEllipsisConfig = false;
|
||||
@Input() blur = false;
|
||||
@Input() style: Record<string, string | number> | null | undefined;
|
||||
@Input() onView: (() => void) | undefined;
|
||||
@Input() renderMention: AngularMentionRenderer | undefined;
|
||||
@Input() renderTag: AngularTagRenderer | undefined;
|
||||
@Input() renderLink: AngularLinkRenderer | undefined;
|
||||
|
||||
@Output() readonly viewed = new EventEmitter<void>();
|
||||
@Output() readonly expandedChange = new EventEmitter<boolean>();
|
||||
}
|
||||
@@ -0,0 +1,571 @@
|
||||
import type { LinkEntity, MentionEntity, TagEntity } from "../core";
|
||||
import type {
|
||||
AngularContentEllipsisConfig,
|
||||
AngularCountEllipsisConfig,
|
||||
AngularLinkRenderer,
|
||||
AngularRenderResult,
|
||||
AngularRowsEllipsisConfig,
|
||||
AngularTagRenderer,
|
||||
AngularMentionRenderer,
|
||||
} from "./types";
|
||||
|
||||
import { NgFor, NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault } from "@angular/common";
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
Output,
|
||||
SimpleChanges,
|
||||
ViewChild,
|
||||
} from "@angular/core";
|
||||
|
||||
import {
|
||||
TachTypographyComponent,
|
||||
type TachTypographyHostProps,
|
||||
} from "@hublib-web/tach-typography/angular";
|
||||
import type { TypographyWeight } from "@hublib-web/tach-typography/core";
|
||||
|
||||
import { findAllEntities } from "../core";
|
||||
import { ContentSuggestionsRenderResultDirective } from "./render-result.directive";
|
||||
|
||||
const READ_MORE_TEXT = "Читать полностью";
|
||||
const BODY_DATA_ATTR = "data-content-suggestions-body";
|
||||
|
||||
const BASE_SELECTABLE_STYLE: Record<string, string> = {
|
||||
whiteSpace: "pre-wrap",
|
||||
WebkitTouchCallout: "default",
|
||||
WebkitUserSelect: "text",
|
||||
KhtmlUserSelect: "text",
|
||||
MozUserSelect: "text",
|
||||
msUserSelect: "text",
|
||||
userSelect: "text",
|
||||
};
|
||||
|
||||
const BLUR_STYLE: Record<string, string> = {
|
||||
filter: "blur(3px)",
|
||||
WebkitUserSelect: "none",
|
||||
KhtmlUserSelect: "none",
|
||||
MozUserSelect: "none",
|
||||
msUserSelect: "none",
|
||||
userSelect: "none",
|
||||
pointerEvents: "none",
|
||||
};
|
||||
|
||||
type TextPart = {
|
||||
kind: "text";
|
||||
key: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
type MentionPart = {
|
||||
kind: "mention";
|
||||
key: string;
|
||||
entity: MentionEntity;
|
||||
index: number;
|
||||
customRender: AngularRenderResult | null;
|
||||
};
|
||||
|
||||
type TagPart = {
|
||||
kind: "tag";
|
||||
key: string;
|
||||
entity: TagEntity;
|
||||
index: number;
|
||||
customRender: AngularRenderResult | null;
|
||||
};
|
||||
|
||||
type LinkPart = {
|
||||
kind: "link";
|
||||
key: string;
|
||||
entity: LinkEntity;
|
||||
index: number;
|
||||
customRender: AngularRenderResult | null;
|
||||
};
|
||||
|
||||
type ContentPart = TextPart | MentionPart | TagPart | LinkPart;
|
||||
|
||||
@Component({
|
||||
selector: "content-text",
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgFor,
|
||||
NgIf,
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
NgSwitchDefault,
|
||||
TachTypographyComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<div #container>
|
||||
<tach-typography
|
||||
as="p"
|
||||
variant="Body"
|
||||
[weight]="weight"
|
||||
[className]="className"
|
||||
[preserveStyle]="mergedStyle"
|
||||
[ellipsis]="rowsEllipsis"
|
||||
[hostProps]="paragraphHostProps"
|
||||
>
|
||||
<ng-container *ngFor="let part of visibleParts; trackBy: trackByKey">
|
||||
<ng-container [ngSwitch]="part.kind">
|
||||
<ng-container *ngSwitchCase="'text'">{{ part.text }}</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'mention'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultMention">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultMention>
|
||||
<tach-typography as="span" variant="Body" color="link" [weight]="weight">
|
||||
{{ part.entity.displayText }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'tag'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultTag">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultTag>
|
||||
<tach-typography as="span" variant="Body" color="link" [weight]="weight">
|
||||
{{ part.entity.text }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'link'">
|
||||
<ng-container *ngIf="part.customRender !== null; else contentSuggestionsDefaultLink">
|
||||
<span [contentSuggestionsRenderResult]="part.customRender"></span>
|
||||
</ng-container>
|
||||
<ng-template #contentSuggestionsDefaultLink>
|
||||
<tach-typography
|
||||
as="a"
|
||||
variant="Body"
|
||||
color="link"
|
||||
[weight]="weight"
|
||||
[hostProps]="buildDefaultLinkHostProps(part.entity.url)"
|
||||
>
|
||||
{{ part.entity.text }}
|
||||
</tach-typography>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchDefault></ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="showTrailingEllipsis">…</ng-container>
|
||||
|
||||
<button
|
||||
*ngIf="showInlineReadMore"
|
||||
type="button"
|
||||
(click)="handleExpand($event)"
|
||||
style="border: 0; background: none; padding: 0; margin-left: 6px; cursor: pointer;"
|
||||
>
|
||||
<tach-typography as="span" variant="Body" color="link" weight="bold">
|
||||
{{ readMoreLabel }}
|
||||
</tach-typography>
|
||||
</button>
|
||||
</tach-typography>
|
||||
|
||||
<button
|
||||
*ngIf="showBlockReadMore"
|
||||
type="button"
|
||||
(click)="handleExpand($event)"
|
||||
style="border: 0; background: none; padding: 0; margin-top: 6px; cursor: pointer;"
|
||||
>
|
||||
<tach-typography as="span" variant="Body" color="link" weight="bold">
|
||||
{{ readMoreLabel }}
|
||||
</tach-typography>
|
||||
</button>
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class ContentTextComponent implements OnChanges, AfterViewInit, OnDestroy {
|
||||
@Input() className: string | undefined;
|
||||
@Input() weight: TypographyWeight = "normal";
|
||||
@Input() text: string | null | undefined;
|
||||
@Input() ellipsis: AngularContentEllipsisConfig = false;
|
||||
@Input() blur = false;
|
||||
@Input() style: Record<string, string | number> | null | undefined;
|
||||
@Input() onView: (() => void) | undefined;
|
||||
@Input() renderMention: AngularMentionRenderer | undefined;
|
||||
@Input() renderTag: AngularTagRenderer | undefined;
|
||||
@Input() renderLink: AngularLinkRenderer | undefined;
|
||||
|
||||
@Output() readonly viewed = new EventEmitter<void>();
|
||||
@Output() readonly expandedChange = new EventEmitter<boolean>();
|
||||
|
||||
@ViewChild("container", { static: true })
|
||||
private readonly containerRef!: ElementRef<HTMLElement>;
|
||||
|
||||
visibleParts: ContentPart[] = [];
|
||||
mergedStyle: Record<string, string | number> = { ...BASE_SELECTABLE_STYLE };
|
||||
rowsEllipsis: { rows: number } | undefined;
|
||||
showTrailingEllipsis = false;
|
||||
showInlineReadMore = false;
|
||||
showBlockReadMore = false;
|
||||
readMoreLabel = READ_MORE_TEXT;
|
||||
paragraphHostProps: TachTypographyHostProps = { [BODY_DATA_ATTR]: "true" };
|
||||
|
||||
private expanded = false;
|
||||
private mergedExpanded = false;
|
||||
private isExpandedControlled = false;
|
||||
private isInsideView = false;
|
||||
private viewedFired = false;
|
||||
private pendingRowsOverflowCheck = false;
|
||||
private intersectionObserver: IntersectionObserver | null = null;
|
||||
|
||||
constructor(
|
||||
private readonly cdr: ChangeDetectorRef,
|
||||
) {}
|
||||
|
||||
ngOnChanges(_changes: SimpleChanges): void {
|
||||
this.recomputeContent();
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.initObserver();
|
||||
this.scheduleRowsOverflowCheck();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.intersectionObserver) {
|
||||
this.intersectionObserver.disconnect();
|
||||
this.intersectionObserver = null;
|
||||
}
|
||||
}
|
||||
|
||||
trackByKey(_index: number, part: ContentPart): string {
|
||||
return part.key;
|
||||
}
|
||||
|
||||
buildDefaultLinkHostProps(url: string): TachTypographyHostProps {
|
||||
return {
|
||||
href: url,
|
||||
target: "_blank",
|
||||
referrerPolicy: "no-referrer",
|
||||
};
|
||||
}
|
||||
|
||||
handleExpand(event: MouseEvent): void {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (!this.isExpandedControlled) {
|
||||
this.expanded = true;
|
||||
}
|
||||
|
||||
this.notifyExpand(event, true);
|
||||
this.recomputeContent();
|
||||
}
|
||||
|
||||
private recomputeContent(): void {
|
||||
const content = this.text ?? "";
|
||||
const entities = findAllEntities(content);
|
||||
const ellipsisConfig = this.resolveEllipsisConfig();
|
||||
|
||||
this.isExpandedControlled = Boolean(
|
||||
ellipsisConfig && typeof ellipsisConfig.expanded === "boolean",
|
||||
);
|
||||
|
||||
if (!this.isExpandedControlled && this.isCountConfig(ellipsisConfig)) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
if (content.length <= count && !this.expanded) {
|
||||
this.expanded = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.mergedExpanded = this.getMergedExpanded(ellipsisConfig, content);
|
||||
this.readMoreLabel = this.resolveEllipsisSymbol(
|
||||
ellipsisConfig?.symbol,
|
||||
this.mergedExpanded,
|
||||
);
|
||||
|
||||
this.mergedStyle = {
|
||||
...BASE_SELECTABLE_STYLE,
|
||||
...(this.blur ? BLUR_STYLE : {}),
|
||||
...(this.style ?? {}),
|
||||
};
|
||||
|
||||
this.visibleParts = this.buildParts(content, entities, null);
|
||||
this.rowsEllipsis = undefined;
|
||||
this.showTrailingEllipsis = false;
|
||||
this.showInlineReadMore = false;
|
||||
this.showBlockReadMore = false;
|
||||
this.pendingRowsOverflowCheck = false;
|
||||
|
||||
if (this.isCountConfig(ellipsisConfig)) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
const shouldTruncate = !this.mergedExpanded && count > 0 && content.length > count;
|
||||
|
||||
if (shouldTruncate) {
|
||||
const cutoff = this.computeEntitySafeCutoff(count, entities);
|
||||
this.visibleParts = this.buildParts(content, entities, cutoff);
|
||||
this.showTrailingEllipsis = true;
|
||||
this.showInlineReadMore = Boolean(ellipsisConfig.expandable);
|
||||
}
|
||||
} else if (this.isRowsConfig(ellipsisConfig) && !this.mergedExpanded) {
|
||||
this.rowsEllipsis = {
|
||||
rows: ellipsisConfig.rows,
|
||||
};
|
||||
|
||||
if (ellipsisConfig.expandable) {
|
||||
this.pendingRowsOverflowCheck = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.emitViewIfNeeded();
|
||||
this.scheduleRowsOverflowCheck();
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
private buildParts(
|
||||
content: string,
|
||||
entities: ReturnType<typeof findAllEntities>,
|
||||
upto: number | null,
|
||||
): ContentPart[] {
|
||||
const parts: ContentPart[] = [];
|
||||
let lastIndex = 0;
|
||||
|
||||
for (const [index, entity] of entities.entries()) {
|
||||
if (upto !== null && entity.start >= upto) {
|
||||
break;
|
||||
}
|
||||
|
||||
const textEnd = upto !== null ? Math.min(entity.start, upto) : entity.start;
|
||||
if (entity.start > lastIndex && lastIndex < textEnd) {
|
||||
parts.push({
|
||||
kind: "text",
|
||||
key: `text-${lastIndex}-${textEnd}`,
|
||||
text: content.slice(lastIndex, textEnd),
|
||||
});
|
||||
}
|
||||
|
||||
if (upto === null || entity.end <= upto) {
|
||||
if (entity.type === "mention") {
|
||||
parts.push({
|
||||
kind: "mention",
|
||||
key: `mention-${entity.start}-${index}`,
|
||||
entity,
|
||||
index,
|
||||
customRender: this.resolveCustomRender(this.renderMention, entity, index),
|
||||
});
|
||||
} else if (entity.type === "tag") {
|
||||
parts.push({
|
||||
kind: "tag",
|
||||
key: `tag-${entity.start}-${index}`,
|
||||
entity,
|
||||
index,
|
||||
customRender: this.resolveCustomRender(this.renderTag, entity, index),
|
||||
});
|
||||
} else {
|
||||
parts.push({
|
||||
kind: "link",
|
||||
key: `link-${entity.start}-${index}`,
|
||||
entity,
|
||||
index,
|
||||
customRender: this.resolveCustomRender(this.renderLink, entity, index),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
lastIndex = entity.end;
|
||||
}
|
||||
|
||||
if (upto === null) {
|
||||
if (lastIndex < content.length) {
|
||||
parts.push({
|
||||
kind: "text",
|
||||
key: `text-${lastIndex}-${content.length}`,
|
||||
text: content.slice(lastIndex),
|
||||
});
|
||||
}
|
||||
} else if (lastIndex < upto) {
|
||||
parts.push({
|
||||
kind: "text",
|
||||
key: `text-${lastIndex}-${upto}`,
|
||||
text: content.slice(lastIndex, upto),
|
||||
});
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
private resolveCustomRender<T>(
|
||||
rendererFn: ((entity: T, index: number) => AngularRenderResult) | undefined,
|
||||
entity: T,
|
||||
index: number,
|
||||
): AngularRenderResult | null {
|
||||
const rendered = rendererFn?.(entity, index);
|
||||
return rendered ?? null;
|
||||
}
|
||||
|
||||
private resolveEllipsisConfig(): AngularCountEllipsisConfig | AngularRowsEllipsisConfig | null {
|
||||
if (!this.ellipsis || typeof this.ellipsis !== "object") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.ellipsis;
|
||||
}
|
||||
|
||||
private isCountConfig(
|
||||
config: AngularCountEllipsisConfig | AngularRowsEllipsisConfig | null,
|
||||
): config is AngularCountEllipsisConfig {
|
||||
return Boolean(config && "count" in config);
|
||||
}
|
||||
|
||||
private isRowsConfig(
|
||||
config: AngularCountEllipsisConfig | AngularRowsEllipsisConfig | null,
|
||||
): config is AngularRowsEllipsisConfig {
|
||||
return Boolean(config && "rows" in config);
|
||||
}
|
||||
|
||||
private getMergedExpanded(
|
||||
config: AngularCountEllipsisConfig | AngularRowsEllipsisConfig | null,
|
||||
text: string,
|
||||
): boolean {
|
||||
const controlledExpanded =
|
||||
config && typeof config.expanded === "boolean"
|
||||
? config.expanded
|
||||
: undefined;
|
||||
|
||||
if (controlledExpanded !== undefined) {
|
||||
return controlledExpanded;
|
||||
}
|
||||
|
||||
if (this.isCountConfig(config)) {
|
||||
const count = config.count ?? 0;
|
||||
if (count <= 0 || text.length <= count) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return this.expanded;
|
||||
}
|
||||
|
||||
private resolveEllipsisSymbol(
|
||||
symbol: AngularCountEllipsisConfig["symbol"] | AngularRowsEllipsisConfig["symbol"],
|
||||
expanded: boolean,
|
||||
): string {
|
||||
if (typeof symbol === "function") {
|
||||
return symbol(expanded);
|
||||
}
|
||||
|
||||
return symbol ?? READ_MORE_TEXT;
|
||||
}
|
||||
|
||||
private notifyExpand(event: MouseEvent, expanded: boolean): void {
|
||||
const config = this.resolveEllipsisConfig();
|
||||
const onExpand = config?.onExpand;
|
||||
|
||||
if (onExpand) {
|
||||
if (onExpand.length >= 2) {
|
||||
(onExpand as (e: MouseEvent, info: { expanded: boolean }) => void)(event, { expanded });
|
||||
} else {
|
||||
(onExpand as (nextExpanded: boolean) => void)(expanded);
|
||||
}
|
||||
}
|
||||
|
||||
this.expandedChange.emit(expanded);
|
||||
}
|
||||
|
||||
private computeEntitySafeCutoff(
|
||||
count: number,
|
||||
entities: ReturnType<typeof findAllEntities>,
|
||||
): number {
|
||||
let cutoff = count;
|
||||
let extended = true;
|
||||
|
||||
while (extended) {
|
||||
extended = false;
|
||||
|
||||
for (const entity of entities) {
|
||||
if (entity.start < cutoff && entity.end > cutoff) {
|
||||
cutoff = entity.end;
|
||||
extended = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cutoff;
|
||||
}
|
||||
|
||||
private initObserver(): void {
|
||||
const host = this.containerRef?.nativeElement;
|
||||
if (!host) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof IntersectionObserver === "undefined") {
|
||||
this.isInsideView = true;
|
||||
this.emitViewIfNeeded();
|
||||
return;
|
||||
}
|
||||
|
||||
this.intersectionObserver = new IntersectionObserver(
|
||||
entries => {
|
||||
const entry = entries[0];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.isIntersecting && !this.isInsideView) {
|
||||
this.isInsideView = true;
|
||||
this.emitViewIfNeeded();
|
||||
}
|
||||
},
|
||||
{ threshold: 0.5 },
|
||||
);
|
||||
|
||||
this.intersectionObserver.observe(host);
|
||||
}
|
||||
|
||||
private emitViewIfNeeded(): void {
|
||||
if (!this.mergedExpanded || this.viewedFired || !this.isInsideView) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.viewedFired = true;
|
||||
this.onView?.();
|
||||
this.viewed.emit();
|
||||
}
|
||||
|
||||
private scheduleRowsOverflowCheck(): void {
|
||||
if (!this.pendingRowsOverflowCheck) {
|
||||
return;
|
||||
}
|
||||
|
||||
queueMicrotask(() => {
|
||||
this.refreshRowsOverflow();
|
||||
});
|
||||
}
|
||||
|
||||
private refreshRowsOverflow(): void {
|
||||
const paragraph = this.getParagraphElement();
|
||||
if (!paragraph) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasOverflow = paragraph.scrollHeight - paragraph.clientHeight > 1;
|
||||
if (this.showBlockReadMore !== hasOverflow) {
|
||||
this.showBlockReadMore = hasOverflow;
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
private getParagraphElement(): HTMLElement | null {
|
||||
const host = this.containerRef?.nativeElement;
|
||||
if (!host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return host.querySelector(`[${BODY_DATA_ATTR}="true"]`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import type {
|
||||
AngularContentEllipsisConfig,
|
||||
AngularLinkRenderer,
|
||||
AngularMentionRenderer,
|
||||
AngularRowsEllipsisConfig,
|
||||
AngularTagRenderer,
|
||||
} from "./types";
|
||||
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
|
||||
import { ContentTextWithSuggestionsComponent } from "./content-text-with-suggestions.component";
|
||||
|
||||
@Component({
|
||||
selector: "content-title-with-suggestions",
|
||||
standalone: true,
|
||||
imports: [ContentTextWithSuggestionsComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<content-text-with-suggestions
|
||||
[className]="className"
|
||||
weight="bold"
|
||||
[text]="text"
|
||||
[ellipsis]="normalizedEllipsis"
|
||||
[blur]="blur"
|
||||
[style]="style"
|
||||
[onView]="onView"
|
||||
[renderMention]="renderMention"
|
||||
[renderTag]="renderTag"
|
||||
[renderLink]="renderLink"
|
||||
(viewed)="viewed.emit()"
|
||||
(expandedChange)="expandedChange.emit($event)"
|
||||
></content-text-with-suggestions>
|
||||
`,
|
||||
})
|
||||
export class ContentTitleWithSuggestionsComponent {
|
||||
@Input() className: string | undefined;
|
||||
@Input() text: string | null | undefined;
|
||||
@Input() ellipsis: AngularContentEllipsisConfig | undefined;
|
||||
@Input() blur = false;
|
||||
@Input() style: Record<string, string | number> | null | undefined;
|
||||
@Input() onView: (() => void) | undefined;
|
||||
@Input() renderMention: AngularMentionRenderer | undefined;
|
||||
@Input() renderTag: AngularTagRenderer | undefined;
|
||||
@Input() renderLink: AngularLinkRenderer | undefined;
|
||||
|
||||
@Output() readonly viewed = new EventEmitter<void>();
|
||||
@Output() readonly expandedChange = new EventEmitter<boolean>();
|
||||
|
||||
get normalizedEllipsis(): AngularContentEllipsisConfig {
|
||||
if (this.ellipsis !== undefined) {
|
||||
return this.ellipsis;
|
||||
}
|
||||
|
||||
return { rows: 2 } satisfies AngularRowsEllipsisConfig;
|
||||
}
|
||||
}
|
||||
97
packages/content-suggestions/src/angular/index.test.ts
Normal file
97
packages/content-suggestions/src/angular/index.test.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import "@angular/compiler";
|
||||
|
||||
import { Component, provideZonelessChangeDetection } from "@angular/core";
|
||||
import { TestBed, getTestBed } from "@angular/core/testing";
|
||||
import { BrowserTestingModule, platformBrowserTesting } from "@angular/platform-browser/testing";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
} from "../../dist/angular/index.js";
|
||||
|
||||
const ensureAngularTestEnvironment = (): void => {
|
||||
try {
|
||||
getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting());
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
if (!message.includes("Cannot set base providers")) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ensureAngularTestEnvironment();
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [ContentTextWithSuggestionsComponent, ContentTitleWithSuggestionsComponent],
|
||||
template: `
|
||||
<content-text-with-suggestions [text]="text"></content-text-with-suggestions>
|
||||
<content-title-with-suggestions [text]="title"></content-title-with-suggestions>
|
||||
`,
|
||||
})
|
||||
class ContentSuggestionsHostComponent {
|
||||
text =
|
||||
"Привет @[John Doe](d290f1ee-6c54-4b01-90e6-d701748f0851) #frontend смотри example.com";
|
||||
title = "Заголовок @[Jane Roe](123e4567-e89b-12d3-a456-426614174000) #news docs.example.com";
|
||||
}
|
||||
|
||||
describe("content-suggestions (angular)", () => {
|
||||
afterEach(() => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
it("renders text with mention, tag and link tokens", async () => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [provideZonelessChangeDetection()],
|
||||
});
|
||||
|
||||
const fixture = TestBed.createComponent(ContentSuggestionsHostComponent);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const host: HTMLElement = fixture.nativeElement;
|
||||
const content = host.querySelector("content-text-with-suggestions");
|
||||
expect(content).not.toBeNull();
|
||||
|
||||
const text = content?.textContent ?? "";
|
||||
expect(text).toContain("Привет");
|
||||
expect(text).toContain("@John Doe");
|
||||
expect(text).toContain("#frontend");
|
||||
expect(text).toContain("example.com");
|
||||
|
||||
const link = content?.querySelector('a.tach-typography[href="https://example.com"]');
|
||||
expect(link).not.toBeNull();
|
||||
expect(link?.textContent).toContain("example.com");
|
||||
});
|
||||
|
||||
it("renders title variant and keeps entities visible", async () => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [provideZonelessChangeDetection()],
|
||||
});
|
||||
|
||||
const fixture = TestBed.createComponent(ContentSuggestionsHostComponent);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const host: HTMLElement = fixture.nativeElement;
|
||||
const title = host.querySelector("content-title-with-suggestions");
|
||||
expect(title).not.toBeNull();
|
||||
|
||||
const text = title?.textContent ?? "";
|
||||
expect(text).toContain("Заголовок");
|
||||
expect(text).toContain("@Jane Roe");
|
||||
expect(text).toContain("#news");
|
||||
expect(text).toContain("docs.example.com");
|
||||
|
||||
const boldNode = title?.querySelector(".tach-typography--bold");
|
||||
expect(boldNode).not.toBeNull();
|
||||
|
||||
const link = title?.querySelector('a.tach-typography[href="https://docs.example.com"]');
|
||||
expect(link).not.toBeNull();
|
||||
expect(link?.textContent).toContain("docs.example.com");
|
||||
});
|
||||
});
|
||||
@@ -1,106 +1,57 @@
|
||||
import type { ContentEntity, LinkEntity, MentionEntity, TagEntity } from "../core";
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { findAllEntities } from "../core";
|
||||
import { ContentTextComponent } from "./content-text.component";
|
||||
import { ContentTextWithSuggestionsComponent } from "./content-text-with-suggestions.component";
|
||||
import { ContentTitleWithSuggestionsComponent } from "./content-title-with-suggestions.component";
|
||||
import { ContentSuggestionsRenderResultDirective } from "./render-result.directive";
|
||||
|
||||
export interface AngularTextToken {
|
||||
kind: "text";
|
||||
text: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
export type {
|
||||
AngularContentEllipsisConfig,
|
||||
AngularContentSnapshot,
|
||||
AngularContentTextProps,
|
||||
AngularContentTextWithSuggestionsProps,
|
||||
AngularContentTitleWithSuggestionsProps,
|
||||
AngularContentToken,
|
||||
AngularCountEllipsisConfig,
|
||||
AngularEllipsisExpandHandler,
|
||||
AngularEllipsisSymbol,
|
||||
AngularExpandInfo,
|
||||
AngularLinkRenderer,
|
||||
AngularLinkToken,
|
||||
AngularMentionRenderer,
|
||||
AngularMentionToken,
|
||||
AngularRenderResult,
|
||||
AngularRowsEllipsisConfig,
|
||||
AngularTagRenderer,
|
||||
AngularTagToken,
|
||||
AngularTextToken,
|
||||
} from "./types";
|
||||
|
||||
export interface AngularMentionToken {
|
||||
kind: "mention";
|
||||
entity: MentionEntity;
|
||||
}
|
||||
export {
|
||||
AngularContentSuggestionsAdapter,
|
||||
buildAngularTagHref,
|
||||
createAngularContentTokens,
|
||||
} from "./tokens";
|
||||
|
||||
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 const buildAngularTagHref = (entity: TagEntity): string => {
|
||||
return `/search/?query=${encodeURIComponent(entity.tag.toLowerCase())}`;
|
||||
export {
|
||||
ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
};
|
||||
|
||||
export const createAngularContentTokens = (
|
||||
inputText: string | null | undefined,
|
||||
): AngularContentToken[] => {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
|
||||
let cursor = 0;
|
||||
const tokens: AngularContentToken[] = [];
|
||||
|
||||
for (const entity of entities) {
|
||||
if (entity.start > cursor) {
|
||||
tokens.push({
|
||||
kind: "text",
|
||||
text: text.slice(cursor, entity.start),
|
||||
start: cursor,
|
||||
end: entity.start,
|
||||
});
|
||||
}
|
||||
|
||||
if (entity.type === "mention") {
|
||||
tokens.push({
|
||||
kind: "mention",
|
||||
entity,
|
||||
});
|
||||
} else if (entity.type === "tag") {
|
||||
tokens.push({
|
||||
kind: "tag",
|
||||
entity,
|
||||
});
|
||||
} else {
|
||||
tokens.push({
|
||||
kind: "link",
|
||||
entity,
|
||||
});
|
||||
}
|
||||
|
||||
cursor = entity.end;
|
||||
}
|
||||
|
||||
if (cursor < text.length) {
|
||||
tokens.push({
|
||||
kind: "text",
|
||||
text: text.slice(cursor),
|
||||
start: cursor,
|
||||
end: text.length,
|
||||
});
|
||||
}
|
||||
|
||||
return tokens;
|
||||
};
|
||||
|
||||
export class AngularContentSuggestionsAdapter {
|
||||
snapshot(inputText: string | null | undefined): AngularContentSnapshot {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
const tokens = createAngularContentTokens(text);
|
||||
|
||||
return {
|
||||
text,
|
||||
entities,
|
||||
tokens,
|
||||
};
|
||||
}
|
||||
}
|
||||
@NgModule({
|
||||
imports: [
|
||||
ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
],
|
||||
exports: [
|
||||
ContentTextComponent,
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
ContentSuggestionsRenderResultDirective,
|
||||
],
|
||||
})
|
||||
export class ContentSuggestionsAngularModule {}
|
||||
|
||||
1
packages/content-suggestions/src/angular/public-api.ts
Normal file
1
packages/content-suggestions/src/angular/public-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./index";
|
||||
@@ -0,0 +1,43 @@
|
||||
import type { AngularRenderResult } from "./types";
|
||||
|
||||
import { Directive, ElementRef, Input, OnChanges, Renderer2 } from "@angular/core";
|
||||
|
||||
@Directive({
|
||||
selector: "[contentSuggestionsRenderResult]",
|
||||
standalone: true,
|
||||
})
|
||||
export class ContentSuggestionsRenderResultDirective implements OnChanges {
|
||||
@Input() contentSuggestionsRenderResult: AngularRenderResult;
|
||||
|
||||
constructor(
|
||||
private readonly elementRef: ElementRef<HTMLElement>,
|
||||
private readonly renderer: Renderer2,
|
||||
) {
|
||||
this.renderer.setStyle(this.elementRef.nativeElement, "display", "contents");
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.clearHost();
|
||||
|
||||
const value = this.contentSuggestionsRenderResult;
|
||||
if (value === null || value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof Node) {
|
||||
this.renderer.appendChild(this.elementRef.nativeElement, value);
|
||||
return;
|
||||
}
|
||||
|
||||
const textNode = this.renderer.createText(String(value));
|
||||
this.renderer.appendChild(this.elementRef.nativeElement, textNode);
|
||||
}
|
||||
|
||||
private clearHost(): void {
|
||||
const host = this.elementRef.nativeElement;
|
||||
|
||||
while (host.firstChild) {
|
||||
host.removeChild(host.firstChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
83
packages/content-suggestions/src/angular/tokens.ts
Normal file
83
packages/content-suggestions/src/angular/tokens.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import type {
|
||||
AngularContentSnapshot,
|
||||
AngularContentToken,
|
||||
AngularLinkToken,
|
||||
AngularMentionToken,
|
||||
AngularTagToken,
|
||||
AngularTextToken,
|
||||
} from "./types";
|
||||
|
||||
import type { ContentEntity, TagEntity } from "../core";
|
||||
|
||||
import { findAllEntities } from "../core";
|
||||
|
||||
const mapEntityToToken = (entity: ContentEntity): AngularMentionToken | AngularTagToken | AngularLinkToken => {
|
||||
if (entity.type === "mention") {
|
||||
return {
|
||||
kind: "mention",
|
||||
entity,
|
||||
};
|
||||
}
|
||||
|
||||
if (entity.type === "tag") {
|
||||
return {
|
||||
kind: "tag",
|
||||
entity,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
kind: "link",
|
||||
entity,
|
||||
};
|
||||
};
|
||||
|
||||
const createTextToken = (text: string, start: number, end: number): AngularTextToken => ({
|
||||
kind: "text",
|
||||
text,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
|
||||
export const createAngularContentTokens = (
|
||||
inputText: string | null | undefined,
|
||||
): AngularContentToken[] => {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
|
||||
let cursor = 0;
|
||||
const tokens: AngularContentToken[] = [];
|
||||
|
||||
for (const entity of entities) {
|
||||
if (entity.start > cursor) {
|
||||
tokens.push(createTextToken(text.slice(cursor, entity.start), cursor, entity.start));
|
||||
}
|
||||
|
||||
tokens.push(mapEntityToToken(entity));
|
||||
cursor = entity.end;
|
||||
}
|
||||
|
||||
if (cursor < text.length) {
|
||||
tokens.push(createTextToken(text.slice(cursor), cursor, text.length));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
};
|
||||
|
||||
export const buildAngularTagHref = (entity: TagEntity): string => {
|
||||
return `/search/?query=${encodeURIComponent(entity.tag.toLowerCase())}`;
|
||||
};
|
||||
|
||||
export class AngularContentSuggestionsAdapter {
|
||||
snapshot(inputText: string | null | undefined): AngularContentSnapshot {
|
||||
const text = inputText ?? "";
|
||||
const entities = findAllEntities(text);
|
||||
const tokens = createAngularContentTokens(text);
|
||||
|
||||
return {
|
||||
text,
|
||||
entities,
|
||||
tokens,
|
||||
};
|
||||
}
|
||||
}
|
||||
113
packages/content-suggestions/src/angular/types.ts
Normal file
113
packages/content-suggestions/src/angular/types.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import type { ContentEntity, LinkEntity, MentionEntity, TagEntity } from "../core";
|
||||
|
||||
import type { TypographyWeight } from "@hublib-web/tach-typography/core";
|
||||
|
||||
export interface AngularTextToken {
|
||||
kind: "text";
|
||||
text: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
export interface AngularMentionToken {
|
||||
kind: "mention";
|
||||
entity: MentionEntity;
|
||||
}
|
||||
|
||||
export interface AngularTagToken {
|
||||
kind: "tag";
|
||||
entity: TagEntity;
|
||||
}
|
||||
|
||||
export interface AngularLinkToken {
|
||||
kind: "link";
|
||||
entity: LinkEntity;
|
||||
}
|
||||
|
||||
export type AngularContentToken =
|
||||
| AngularTextToken
|
||||
| AngularMentionToken
|
||||
| AngularTagToken
|
||||
| AngularLinkToken;
|
||||
|
||||
export interface AngularContentSnapshot {
|
||||
text: string;
|
||||
entities: ContentEntity[];
|
||||
tokens: AngularContentToken[];
|
||||
}
|
||||
|
||||
export interface AngularExpandInfo {
|
||||
expanded: boolean;
|
||||
}
|
||||
|
||||
export type AngularEllipsisSymbol = string | ((expanded: boolean) => string);
|
||||
|
||||
export type AngularEllipsisExpandHandler =
|
||||
| ((expanded: boolean) => void)
|
||||
| ((event: MouseEvent, info: AngularExpandInfo) => void);
|
||||
|
||||
export type AngularCountEllipsisConfig = {
|
||||
count: number;
|
||||
rows?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: AngularEllipsisExpandHandler;
|
||||
};
|
||||
|
||||
export type AngularRowsEllipsisConfig = {
|
||||
rows: number;
|
||||
count?: never;
|
||||
expandable?: boolean;
|
||||
expanded?: boolean;
|
||||
symbol?: AngularEllipsisSymbol;
|
||||
onExpand?: AngularEllipsisExpandHandler;
|
||||
};
|
||||
|
||||
export type AngularContentEllipsisConfig =
|
||||
| AngularCountEllipsisConfig
|
||||
| AngularRowsEllipsisConfig
|
||||
| false;
|
||||
|
||||
export type AngularRenderResult = Node | string | number | null | undefined;
|
||||
|
||||
export type AngularMentionRenderer = (
|
||||
entity: MentionEntity,
|
||||
index: number,
|
||||
) => AngularRenderResult;
|
||||
|
||||
export type AngularTagRenderer = (
|
||||
entity: TagEntity,
|
||||
index: number,
|
||||
) => AngularRenderResult;
|
||||
|
||||
export type AngularLinkRenderer = (
|
||||
entity: LinkEntity,
|
||||
index: number,
|
||||
) => AngularRenderResult;
|
||||
|
||||
export interface AngularContentTextProps {
|
||||
className?: string;
|
||||
weight?: TypographyWeight;
|
||||
text?: string | null;
|
||||
ellipsis?: AngularContentEllipsisConfig;
|
||||
blur?: boolean;
|
||||
style?: Record<string, string | number> | null;
|
||||
onView?: () => void;
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
renderLink?: AngularLinkRenderer;
|
||||
}
|
||||
|
||||
export type AngularContentTextWithSuggestionsProps = Omit<
|
||||
AngularContentTextProps,
|
||||
"renderMention" | "renderTag"
|
||||
> & {
|
||||
renderMention?: AngularMentionRenderer;
|
||||
renderTag?: AngularTagRenderer;
|
||||
};
|
||||
|
||||
export type AngularContentTitleWithSuggestionsProps = Omit<
|
||||
AngularContentTextWithSuggestionsProps,
|
||||
"weight"
|
||||
>;
|
||||
@@ -4,8 +4,10 @@ import React from "react";
|
||||
|
||||
import { ContentTextWithSuggestions } from "./content-text-with-suggestions";
|
||||
|
||||
interface ContentTitleWithSuggestionsProps
|
||||
extends Omit<ComponentProps<typeof ContentTextWithSuggestions>, "weight"> {}
|
||||
type ContentTitleWithSuggestionsProps = Omit<
|
||||
ComponentProps<typeof ContentTextWithSuggestions>,
|
||||
"weight"
|
||||
>;
|
||||
|
||||
export const ContentTitleWithSuggestions = ({
|
||||
text,
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
import "@angular/compiler";
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
|
||||
import { createComponent, provideZonelessChangeDetection, type ApplicationRef } from "@angular/core";
|
||||
import { createApplication } from "@angular/platform-browser";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { expect, waitFor, within } from "@storybook/test";
|
||||
|
||||
import {
|
||||
ContentTextWithSuggestionsComponent,
|
||||
ContentTitleWithSuggestionsComponent,
|
||||
} from "../../dist/angular/index.js";
|
||||
|
||||
const meta = {
|
||||
title: "Angular/ContentSuggestions DOM",
|
||||
tags: ["autodocs"],
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"Angular runtime verification in Storybook: text and title components with mention/tag/link rendered into real DOM.",
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies Meta;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
type MountedTextComponent = ReturnType<typeof createComponent<ContentTextWithSuggestionsComponent>>;
|
||||
type MountedTitleComponent = ReturnType<typeof createComponent<ContentTitleWithSuggestionsComponent>>;
|
||||
|
||||
const TEXT_SAMPLE =
|
||||
"Angular text: @[Иван](123e4567-e89b-12d3-a456-426614174000) #frontend docs.example.com";
|
||||
const TITLE_SAMPLE =
|
||||
"Angular title: @[Мария](123e4567-e89b-12d3-a456-426614174001) #release github.com";
|
||||
|
||||
const createSection = (title: string): HTMLDivElement => {
|
||||
const section = document.createElement("div");
|
||||
section.style.display = "grid";
|
||||
section.style.gap = "8px";
|
||||
section.style.padding = "10px 12px";
|
||||
section.style.border = "1px solid #e2e8f0";
|
||||
section.style.borderRadius = "10px";
|
||||
|
||||
const heading = document.createElement("div");
|
||||
heading.style.fontFamily =
|
||||
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Courier New", monospace';
|
||||
heading.style.fontSize = "12px";
|
||||
heading.style.color = "#475569";
|
||||
heading.textContent = title;
|
||||
|
||||
const body = document.createElement("div");
|
||||
section.append(heading, body);
|
||||
body.dataset.slot = "body";
|
||||
|
||||
return section;
|
||||
};
|
||||
|
||||
const AngularContentDomHarness: React.FC = () => {
|
||||
const hostRef = useRef<HTMLDivElement | null>(null);
|
||||
const [status, setStatus] = useState("mounting");
|
||||
|
||||
useEffect(() => {
|
||||
const host = hostRef.current;
|
||||
if (!host) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
let appRef: ApplicationRef | null = null;
|
||||
let textRef: MountedTextComponent | null = null;
|
||||
let titleRef: MountedTitleComponent | null = null;
|
||||
|
||||
host.innerHTML = "";
|
||||
setStatus("mounting");
|
||||
|
||||
void createApplication({
|
||||
providers: [provideZonelessChangeDetection()],
|
||||
})
|
||||
.then(app => {
|
||||
if (cancelled) {
|
||||
app.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
appRef = app;
|
||||
|
||||
const textSection = createSection("ContentTextWithSuggestionsComponent");
|
||||
const titleSection = createSection("ContentTitleWithSuggestionsComponent");
|
||||
textSection.dataset.testid = "angular-content-text";
|
||||
titleSection.dataset.testid = "angular-content-title";
|
||||
host.append(textSection, titleSection);
|
||||
|
||||
const textHost = textSection.querySelector('[data-slot="body"]') as HTMLElement;
|
||||
const titleHost = titleSection.querySelector('[data-slot="body"]') as HTMLElement;
|
||||
|
||||
textRef = createComponent(ContentTextWithSuggestionsComponent, {
|
||||
environmentInjector: app.injector,
|
||||
hostElement: textHost,
|
||||
});
|
||||
app.attachView(textRef.hostView);
|
||||
textRef.setInput("text", TEXT_SAMPLE);
|
||||
textRef.setInput("ellipsis", false);
|
||||
textRef.changeDetectorRef.detectChanges();
|
||||
|
||||
titleRef = createComponent(ContentTitleWithSuggestionsComponent, {
|
||||
environmentInjector: app.injector,
|
||||
hostElement: titleHost,
|
||||
});
|
||||
app.attachView(titleRef.hostView);
|
||||
titleRef.setInput("text", TITLE_SAMPLE);
|
||||
titleRef.setInput("ellipsis", false);
|
||||
titleRef.changeDetectorRef.detectChanges();
|
||||
|
||||
setStatus("ready");
|
||||
})
|
||||
.catch(error => {
|
||||
setStatus(`error: ${String(error)}`);
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
textRef?.destroy();
|
||||
titleRef?.destroy();
|
||||
appRef?.destroy();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ maxWidth: 960, width: "100%", display: "grid", gap: 12 }}
|
||||
data-testid="angular-content-dom-story"
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
||||
fontSize: 12,
|
||||
color: "#475569",
|
||||
}}
|
||||
>
|
||||
status={status}
|
||||
</div>
|
||||
<div ref={hostRef} style={{ display: "grid", gap: 12 }} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const TextAndTitleRenderCheck: Story = {
|
||||
render: () => <AngularContentDomHarness />,
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(canvas.getByText("status=ready")).toBeTruthy();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(canvas.getByText("Angular text:")).toBeTruthy();
|
||||
expect(canvas.getByText("@Иван")).toBeTruthy();
|
||||
expect(canvas.getByText("#frontend")).toBeTruthy();
|
||||
expect(canvas.getByText("docs.example.com")).toBeTruthy();
|
||||
expect(canvas.getByText("Angular title:")).toBeTruthy();
|
||||
expect(canvas.getByText("@Мария")).toBeTruthy();
|
||||
expect(canvas.getByText("#release")).toBeTruthy();
|
||||
expect(canvas.getByText("github.com")).toBeTruthy();
|
||||
});
|
||||
|
||||
const docsLink = canvas.getByRole("link", { name: "docs.example.com" });
|
||||
expect(docsLink.getAttribute("href")).toBe("https://docs.example.com");
|
||||
|
||||
const githubLink = canvas.getByRole("link", { name: "github.com" });
|
||||
expect(githubLink.getAttribute("href")).toBe("https://github.com");
|
||||
},
|
||||
};
|
||||
20
packages/content-suggestions/tsconfig.angular.json
Normal file
20
packages/content-suggestions/tsconfig.angular.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist/angular",
|
||||
"types": ["node"],
|
||||
"module": "ES2022",
|
||||
"target": "ES2022",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"importHelpers": true
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"compilationMode": "partial"
|
||||
},
|
||||
"files": ["src/angular/public-api.ts"],
|
||||
"include": ["src/angular/**/*.ts", "src/core/**/*.ts"],
|
||||
"exclude": ["dist", "src/**/*.test.ts", "src/react", "src/stories"]
|
||||
}
|
||||
@@ -4,7 +4,6 @@ export default defineConfig({
|
||||
entry: {
|
||||
"core/index": "src/core/index.ts",
|
||||
"react/index": "src/react/index.tsx",
|
||||
"angular/index": "src/angular/index.ts",
|
||||
},
|
||||
format: ["esm", "cjs"],
|
||||
dts: true,
|
||||
@@ -21,5 +20,6 @@ export default defineConfig({
|
||||
"@angular/core",
|
||||
"@angular/common",
|
||||
"@hublib-web/tach-typography/react",
|
||||
"@hublib-web/tach-typography/angular",
|
||||
],
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ Typography package with shared tokens and framework adapters:
|
||||
## Install from Git (SSH tag)
|
||||
|
||||
```bash
|
||||
yarn add "@hublib-web/tach-typography@git+ssh://git@github.com/ORG/REPO.git#workspace=@hublib-web/tach-typography&tag=tach-typography-v0.1.0"
|
||||
yarn add "@hublib-web/tach-typography@git+ssh://git@github.com/ORG/REPO.git#workspace=@hublib-web/tach-typography&tag=tach-typography-v0.2.0"
|
||||
```
|
||||
|
||||
## Install inside this monorepo
|
||||
@@ -30,13 +30,13 @@ yarn workspace @hublib-web/tach-typography build
|
||||
|
||||
```bash
|
||||
git add packages/tach-typography/package.json packages/tach-typography/dist
|
||||
git commit -m "release(tach-typography): v0.1.0"
|
||||
git commit -m "release(tach-typography): v0.2.0"
|
||||
```
|
||||
|
||||
4. Create and push tag:
|
||||
|
||||
```bash
|
||||
git tag -a tach-typography-v0.1.0 -m "@hublib-web/tach-typography v0.1.0"
|
||||
git tag -a tach-typography-v0.2.0 -m "@hublib-web/tach-typography v0.2.0"
|
||||
git push origin main --follow-tags
|
||||
```
|
||||
|
||||
@@ -58,31 +58,54 @@ export const Example = () => (
|
||||
);
|
||||
```
|
||||
|
||||
Markdown as a prop (keeps existing API):
|
||||
|
||||
```tsx
|
||||
<TachTypography.Text.AccentH1 markdownEnabled>
|
||||
**Bold** _italic_ [Docs](https://example.com)
|
||||
</TachTypography.Text.AccentH1>
|
||||
```
|
||||
|
||||
## Angular usage (NG-ZORRO)
|
||||
|
||||
```ts
|
||||
import { Component } from "@angular/core";
|
||||
import { TachTypographyDirective, TachTypographyNzModule } from "@hublib-web/tach-typography/angular";
|
||||
import { TachTypographyComponent } from "@hublib-web/tach-typography/angular";
|
||||
|
||||
@Component({
|
||||
selector: "app-example",
|
||||
standalone: true,
|
||||
imports: [TachTypographyNzModule, TachTypographyDirective],
|
||||
imports: [TachTypographyComponent],
|
||||
template: `
|
||||
<span
|
||||
nz-typography
|
||||
tachTypography
|
||||
tachTypography="Body"
|
||||
tachTypographyColor="link"
|
||||
tachTypographyWeight="bold"
|
||||
<tach-typography
|
||||
variant="Body"
|
||||
color="link"
|
||||
weight="bold"
|
||||
[nzProps]="{ nzCopyable: true, nzType: 'secondary' }"
|
||||
>
|
||||
Hello from Angular + NG-ZORRO
|
||||
</span>
|
||||
</tach-typography>
|
||||
|
||||
<tach-typography
|
||||
as="a"
|
||||
variant="Body"
|
||||
color="link"
|
||||
[hostProps]="{ href: '/docs', target: '_blank', rel: 'noopener noreferrer' }"
|
||||
[nzProps]="{ nzType: 'secondary' }"
|
||||
>
|
||||
Open docs
|
||||
</tach-typography>
|
||||
`,
|
||||
})
|
||||
export class ExampleComponent {}
|
||||
```
|
||||
|
||||
Markdown in Angular:
|
||||
|
||||
```html
|
||||
<tach-typography variant="Body" [markdownEnabled]="true" [markdown]="'**Bold** _italic_'"></tach-typography>
|
||||
```
|
||||
|
||||
## Storybook (dev/design system)
|
||||
|
||||
Run from repository root:
|
||||
|
||||
85
packages/tach-typography/dist/angular/angular/index.d.ts
vendored
Normal file
85
packages/tach-typography/dist/angular/angular/index.d.ts
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
import { ElementRef, EventEmitter, OnChanges, Renderer2, SimpleChanges } from "@angular/core";
|
||||
import { NzTypographyComponent } from "ng-zorro-antd/typography";
|
||||
import { type EllipsisOptions, type TypographyClassOptions, type TypographyColor, type TypographyRenderOptions, type TypographyVariant, type TypographyWeight } from "../core";
|
||||
import * as i0 from "@angular/core";
|
||||
import * as i1 from "ng-zorro-antd/typography";
|
||||
export type AngularTypographyClassInput = TypographyClassOptions;
|
||||
export interface AngularTypographyRenderOptions extends TypographyRenderOptions {
|
||||
preserveStyle?: Record<string, string | number>;
|
||||
}
|
||||
export type TachTypographyHostTag = "span" | "p" | "a" | "h1" | "h2" | "h3" | "h4";
|
||||
type NonFunctionNonEmitterKeys<T> = {
|
||||
[K in keyof T]-?: T[K] extends (...args: never[]) => unknown ? never : T[K] extends EventEmitter<unknown> ? never : K;
|
||||
}[keyof T];
|
||||
type NzTypographyInputKey = Extract<NonFunctionNonEmitterKeys<NzTypographyComponent>, `nz${string}`>;
|
||||
export type TachTypographyNzProps = Partial<Pick<NzTypographyComponent, NzTypographyInputKey>>;
|
||||
export type TachTypographyHostProps = Record<string, unknown>;
|
||||
export declare const tachAngularTypographyClassName: (options?: AngularTypographyClassInput) => string;
|
||||
export declare const tachAngularTypographyClassList: (options?: AngularTypographyClassInput) => string[];
|
||||
export declare const tachAngularTypographyStyles: (ellipsis?: EllipsisOptions, preserveStyle?: Record<string, string | number>) => Record<string, string | number>;
|
||||
export declare class TachTypographyDirective implements OnChanges {
|
||||
private readonly elementRef;
|
||||
private readonly renderer;
|
||||
tachTypography: TypographyVariant | "" | undefined;
|
||||
tachTypographyVariant: TypographyVariant;
|
||||
tachTypographyColor: TypographyColor;
|
||||
tachTypographyWeight: TypographyWeight;
|
||||
tachTypographyClickable: boolean;
|
||||
tachTypographyClassName: string | undefined;
|
||||
tachTypographyEllipsis: EllipsisOptions | undefined;
|
||||
private readonly appliedClasses;
|
||||
private readonly appliedStyleProperties;
|
||||
constructor(elementRef: ElementRef<HTMLElement>, renderer: Renderer2);
|
||||
ngOnChanges(_changes: SimpleChanges): void;
|
||||
private syncClasses;
|
||||
private syncEllipsisStyles;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyDirective, never>;
|
||||
static ɵdir: i0.ɵɵDirectiveDeclaration<TachTypographyDirective, "[tachTypography]", never, { "tachTypography": { "alias": "tachTypography"; "required": false; }; "tachTypographyVariant": { "alias": "tachTypographyVariant"; "required": false; }; "tachTypographyColor": { "alias": "tachTypographyColor"; "required": false; }; "tachTypographyWeight": { "alias": "tachTypographyWeight"; "required": false; }; "tachTypographyClickable": { "alias": "tachTypographyClickable"; "required": false; }; "tachTypographyClassName": { "alias": "tachTypographyClassName"; "required": false; }; "tachTypographyEllipsis": { "alias": "tachTypographyEllipsis"; "required": false; }; }, {}, never, never, true, never>;
|
||||
}
|
||||
export declare class TachTypographyNzPropsDirective implements OnChanges {
|
||||
tachTypographyNzProps: TachTypographyNzProps | null | undefined;
|
||||
private readonly appliedNzKeys;
|
||||
private readonly nzTypography;
|
||||
ngOnChanges(): void;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyNzPropsDirective, never>;
|
||||
static ɵdir: i0.ɵɵDirectiveDeclaration<TachTypographyNzPropsDirective, "[tachTypographyNzProps]", never, { "tachTypographyNzProps": { "alias": "tachTypographyNzProps"; "required": false; }; }, {}, never, never, true, never>;
|
||||
}
|
||||
export declare class TachTypographyHostPropsDirective implements OnChanges {
|
||||
private readonly elementRef;
|
||||
private readonly renderer;
|
||||
tachTypographyHostProps: TachTypographyHostProps | null | undefined;
|
||||
private readonly appliedHostProps;
|
||||
constructor(elementRef: ElementRef<HTMLElement>, renderer: Renderer2);
|
||||
ngOnChanges(): void;
|
||||
private shouldApplyAsAttribute;
|
||||
private hasPropertyOnElement;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyHostPropsDirective, never>;
|
||||
static ɵdir: i0.ɵɵDirectiveDeclaration<TachTypographyHostPropsDirective, "[tachTypographyHostProps]", never, { "tachTypographyHostProps": { "alias": "tachTypographyHostProps"; "required": false; }; }, {}, never, never, true, never>;
|
||||
}
|
||||
export declare class TachTypographyComponent implements OnChanges {
|
||||
hostTag: TachTypographyHostTag;
|
||||
variant: TypographyVariant;
|
||||
color: TypographyColor;
|
||||
weight: TypographyWeight;
|
||||
clickable: boolean;
|
||||
markdownEnabled: boolean;
|
||||
markdown: string | undefined;
|
||||
className: string | undefined;
|
||||
ellipsis: EllipsisOptions | undefined;
|
||||
nzProps: TachTypographyNzProps | undefined;
|
||||
hostProps: TachTypographyHostProps | undefined;
|
||||
preserveStyle: Record<string, string | number> | undefined;
|
||||
readonly tachClick: EventEmitter<MouseEvent>;
|
||||
renderedMarkdown: string;
|
||||
ngOnChanges(_changes: SimpleChanges): void;
|
||||
handleClick(event: MouseEvent): void;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyComponent, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<TachTypographyComponent, "tach-typography", never, { "hostTag": { "alias": "as"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "color": { "alias": "color"; "required": false; }; "weight": { "alias": "weight"; "required": false; }; "clickable": { "alias": "clickable"; "required": false; }; "markdownEnabled": { "alias": "markdownEnabled"; "required": false; }; "markdown": { "alias": "markdown"; "required": false; }; "className": { "alias": "className"; "required": false; }; "ellipsis": { "alias": "ellipsis"; "required": false; }; "nzProps": { "alias": "nzProps"; "required": false; }; "hostProps": { "alias": "hostProps"; "required": false; }; "preserveStyle": { "alias": "preserveStyle"; "required": false; }; }, { "tachClick": "tachClick"; }, never, ["*"], true, never>;
|
||||
}
|
||||
export declare class TachTypographyNzModule {
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyNzModule, never>;
|
||||
static ɵmod: i0.ɵɵNgModuleDeclaration<TachTypographyNzModule, never, [typeof i1.NzTypographyModule, typeof TachTypographyDirective, typeof TachTypographyNzPropsDirective, typeof TachTypographyHostPropsDirective, typeof TachTypographyComponent], [typeof i1.NzTypographyModule, typeof TachTypographyDirective, typeof TachTypographyNzPropsDirective, typeof TachTypographyHostPropsDirective, typeof TachTypographyComponent]>;
|
||||
static ɵinj: i0.ɵɵInjectorDeclaration<TachTypographyNzModule>;
|
||||
}
|
||||
export {};
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
packages/tach-typography/dist/angular/angular/index.d.ts.map
vendored
Normal file
1
packages/tach-typography/dist/angular/angular/index.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/angular/index.ts"],"names":[],"mappings":"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"}
|
||||
600
packages/tach-typography/dist/angular/angular/index.js
vendored
Normal file
600
packages/tach-typography/dist/angular/angular/index.js
vendored
Normal file
@@ -0,0 +1,600 @@
|
||||
import { NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet } from "@angular/common";
|
||||
import { ChangeDetectionStrategy, Component, Directive, EventEmitter, inject, Input, NgModule, Output, } from "@angular/core";
|
||||
import { NzTypographyComponent, NzTypographyModule } from "ng-zorro-antd/typography";
|
||||
import { tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle, tachTypographyMarkdownToHtml, } from "../core";
|
||||
import * as i0 from "@angular/core";
|
||||
import * as i1 from "ng-zorro-antd/typography";
|
||||
const camelToKebab = (value) => value.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
|
||||
const toCssProperty = (styleKey) => {
|
||||
if (styleKey.startsWith("Webkit")) {
|
||||
return `-webkit-${camelToKebab(styleKey.slice(6))}`;
|
||||
}
|
||||
return camelToKebab(styleKey);
|
||||
};
|
||||
export const tachAngularTypographyClassName = (options = {}) => {
|
||||
return tachTypographyClassName(options);
|
||||
};
|
||||
export const tachAngularTypographyClassList = (options = {}) => {
|
||||
return tachTypographyClassList(options);
|
||||
};
|
||||
export const tachAngularTypographyStyles = (ellipsis, preserveStyle = {}) => {
|
||||
const ellipsisStyle = tachTypographyEllipsisStyle(ellipsis);
|
||||
if (!ellipsisStyle) {
|
||||
return preserveStyle;
|
||||
}
|
||||
return {
|
||||
...ellipsisStyle,
|
||||
...preserveStyle,
|
||||
};
|
||||
};
|
||||
export class TachTypographyDirective {
|
||||
elementRef;
|
||||
renderer;
|
||||
tachTypography;
|
||||
tachTypographyVariant = "Body";
|
||||
tachTypographyColor = "primary";
|
||||
tachTypographyWeight = "normal";
|
||||
tachTypographyClickable = false;
|
||||
tachTypographyClassName;
|
||||
tachTypographyEllipsis;
|
||||
appliedClasses = new Set();
|
||||
appliedStyleProperties = new Set();
|
||||
constructor(elementRef, renderer) {
|
||||
this.elementRef = elementRef;
|
||||
this.renderer = renderer;
|
||||
}
|
||||
ngOnChanges(_changes) {
|
||||
this.syncClasses();
|
||||
this.syncEllipsisStyles();
|
||||
}
|
||||
syncClasses() {
|
||||
const nextClassList = tachTypographyClassList({
|
||||
variant: this.tachTypography || this.tachTypographyVariant,
|
||||
color: this.tachTypographyColor,
|
||||
weight: this.tachTypographyWeight,
|
||||
clickable: this.tachTypographyClickable,
|
||||
className: this.tachTypographyClassName,
|
||||
});
|
||||
const nextSet = new Set(nextClassList);
|
||||
for (const className of this.appliedClasses) {
|
||||
if (!nextSet.has(className)) {
|
||||
this.renderer.removeClass(this.elementRef.nativeElement, className);
|
||||
}
|
||||
}
|
||||
for (const className of nextSet) {
|
||||
this.renderer.addClass(this.elementRef.nativeElement, className);
|
||||
}
|
||||
this.appliedClasses.clear();
|
||||
for (const className of nextSet) {
|
||||
this.appliedClasses.add(className);
|
||||
}
|
||||
}
|
||||
syncEllipsisStyles() {
|
||||
const nextStyles = tachTypographyEllipsisStyle(this.tachTypographyEllipsis) || {};
|
||||
const nextStyleKeys = new Set(Object.keys(nextStyles));
|
||||
for (const styleKey of this.appliedStyleProperties) {
|
||||
if (!nextStyleKeys.has(styleKey)) {
|
||||
this.renderer.removeStyle(this.elementRef.nativeElement, toCssProperty(styleKey));
|
||||
}
|
||||
}
|
||||
for (const [styleKey, styleValue] of Object.entries(nextStyles)) {
|
||||
this.renderer.setStyle(this.elementRef.nativeElement, toCssProperty(styleKey), styleValue);
|
||||
}
|
||||
this.appliedStyleProperties.clear();
|
||||
for (const styleKey of nextStyleKeys) {
|
||||
this.appliedStyleProperties.add(styleKey);
|
||||
}
|
||||
}
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
|
||||
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.18", type: TachTypographyDirective, isStandalone: true, selector: "[tachTypography]", inputs: { tachTypography: "tachTypography", tachTypographyVariant: "tachTypographyVariant", tachTypographyColor: "tachTypographyColor", tachTypographyWeight: "tachTypographyWeight", tachTypographyClickable: "tachTypographyClickable", tachTypographyClassName: "tachTypographyClassName", tachTypographyEllipsis: "tachTypographyEllipsis" }, usesOnChanges: true, ngImport: i0 });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyDirective, decorators: [{
|
||||
type: Directive,
|
||||
args: [{
|
||||
selector: "[tachTypography]",
|
||||
standalone: true,
|
||||
}]
|
||||
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { tachTypography: [{
|
||||
type: Input
|
||||
}], tachTypographyVariant: [{
|
||||
type: Input
|
||||
}], tachTypographyColor: [{
|
||||
type: Input
|
||||
}], tachTypographyWeight: [{
|
||||
type: Input
|
||||
}], tachTypographyClickable: [{
|
||||
type: Input
|
||||
}], tachTypographyClassName: [{
|
||||
type: Input
|
||||
}], tachTypographyEllipsis: [{
|
||||
type: Input
|
||||
}] } });
|
||||
export class TachTypographyNzPropsDirective {
|
||||
tachTypographyNzProps;
|
||||
appliedNzKeys = new Set();
|
||||
nzTypography = inject(NzTypographyComponent, { self: true, optional: true });
|
||||
ngOnChanges() {
|
||||
if (!this.nzTypography) {
|
||||
return;
|
||||
}
|
||||
const nzTypography = this.nzTypography;
|
||||
const nextProps = this.tachTypographyNzProps ?? {};
|
||||
const nextKeys = new Set();
|
||||
for (const [key, value] of Object.entries(nextProps)) {
|
||||
if (!key.startsWith("nz")) {
|
||||
continue;
|
||||
}
|
||||
nzTypography[key] = value;
|
||||
nextKeys.add(key);
|
||||
}
|
||||
for (const key of this.appliedNzKeys) {
|
||||
if (!nextKeys.has(key)) {
|
||||
nzTypography[key] = undefined;
|
||||
}
|
||||
}
|
||||
this.appliedNzKeys.clear();
|
||||
for (const key of nextKeys) {
|
||||
this.appliedNzKeys.add(key);
|
||||
}
|
||||
}
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyNzPropsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
||||
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.18", type: TachTypographyNzPropsDirective, isStandalone: true, selector: "[tachTypographyNzProps]", inputs: { tachTypographyNzProps: "tachTypographyNzProps" }, usesOnChanges: true, ngImport: i0 });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyNzPropsDirective, decorators: [{
|
||||
type: Directive,
|
||||
args: [{
|
||||
selector: "[tachTypographyNzProps]",
|
||||
standalone: true,
|
||||
}]
|
||||
}], propDecorators: { tachTypographyNzProps: [{
|
||||
type: Input
|
||||
}] } });
|
||||
export class TachTypographyHostPropsDirective {
|
||||
elementRef;
|
||||
renderer;
|
||||
tachTypographyHostProps;
|
||||
appliedHostProps = new Map();
|
||||
constructor(elementRef, renderer) {
|
||||
this.elementRef = elementRef;
|
||||
this.renderer = renderer;
|
||||
}
|
||||
ngOnChanges() {
|
||||
const nextProps = this.tachTypographyHostProps ?? {};
|
||||
const nextAppliedProps = new Map();
|
||||
for (const [key, value] of Object.entries(nextProps)) {
|
||||
if (key === "class" || key === "className" || key === "style") {
|
||||
continue;
|
||||
}
|
||||
if (value === undefined || value === null) {
|
||||
continue;
|
||||
}
|
||||
const applyAsAttr = this.shouldApplyAsAttribute(key, value);
|
||||
if (applyAsAttr) {
|
||||
if (typeof value === "boolean") {
|
||||
if (value) {
|
||||
this.renderer.setAttribute(this.elementRef.nativeElement, key, "");
|
||||
}
|
||||
else {
|
||||
this.renderer.removeAttribute(this.elementRef.nativeElement, key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.renderer.setAttribute(this.elementRef.nativeElement, key, String(value));
|
||||
}
|
||||
nextAppliedProps.set(key, "attr");
|
||||
continue;
|
||||
}
|
||||
this.renderer.setProperty(this.elementRef.nativeElement, key, value);
|
||||
nextAppliedProps.set(key, "prop");
|
||||
}
|
||||
for (const [key, kind] of this.appliedHostProps.entries()) {
|
||||
if (nextAppliedProps.has(key)) {
|
||||
continue;
|
||||
}
|
||||
if (kind === "attr") {
|
||||
this.renderer.removeAttribute(this.elementRef.nativeElement, key);
|
||||
}
|
||||
else {
|
||||
this.renderer.setProperty(this.elementRef.nativeElement, key, undefined);
|
||||
}
|
||||
}
|
||||
this.appliedHostProps.clear();
|
||||
for (const [key, kind] of nextAppliedProps.entries()) {
|
||||
this.appliedHostProps.set(key, kind);
|
||||
}
|
||||
}
|
||||
shouldApplyAsAttribute(key, value) {
|
||||
if (key.startsWith("data-") || key.startsWith("aria-")) {
|
||||
return true;
|
||||
}
|
||||
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
||||
return !this.hasPropertyOnElement(key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
hasPropertyOnElement(key) {
|
||||
return key in this.elementRef.nativeElement;
|
||||
}
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyHostPropsDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
|
||||
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.18", type: TachTypographyHostPropsDirective, isStandalone: true, selector: "[tachTypographyHostProps]", inputs: { tachTypographyHostProps: "tachTypographyHostProps" }, usesOnChanges: true, ngImport: i0 });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyHostPropsDirective, decorators: [{
|
||||
type: Directive,
|
||||
args: [{
|
||||
selector: "[tachTypographyHostProps]",
|
||||
standalone: true,
|
||||
}]
|
||||
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { tachTypographyHostProps: [{
|
||||
type: Input
|
||||
}] } });
|
||||
export class TachTypographyComponent {
|
||||
hostTag = "span";
|
||||
variant = "Body";
|
||||
color = "primary";
|
||||
weight = "normal";
|
||||
clickable = false;
|
||||
markdownEnabled = false;
|
||||
markdown;
|
||||
className;
|
||||
ellipsis;
|
||||
nzProps;
|
||||
hostProps;
|
||||
preserveStyle;
|
||||
tachClick = new EventEmitter();
|
||||
renderedMarkdown = "";
|
||||
ngOnChanges(_changes) {
|
||||
this.renderedMarkdown = this.markdownEnabled
|
||||
? tachTypographyMarkdownToHtml(this.markdown ?? "")
|
||||
: "";
|
||||
}
|
||||
handleClick(event) {
|
||||
this.tachClick.emit(event);
|
||||
}
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: TachTypographyComponent, isStandalone: true, selector: "tach-typography", inputs: { hostTag: ["as", "hostTag"], variant: "variant", color: "color", weight: "weight", clickable: "clickable", markdownEnabled: "markdownEnabled", markdown: "markdown", className: "className", ellipsis: "ellipsis", nzProps: "nzProps", hostProps: "hostProps", preserveStyle: "preserveStyle" }, outputs: { tachClick: "tachClick" }, usesOnChanges: true, ngImport: i0, template: `
|
||||
<ng-template #tachTypographyProjectedContent>
|
||||
<ng-content></ng-content>
|
||||
</ng-template>
|
||||
<ng-template #tachTypographyMarkdownContent>
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-template>
|
||||
|
||||
<ng-container [ngSwitch]="hostTag">
|
||||
<p
|
||||
*ngSwitchCase="'p'"
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</p>
|
||||
<a
|
||||
*ngSwitchCase="'a'"
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</a>
|
||||
<h1
|
||||
*ngSwitchCase="'h1'"
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</h1>
|
||||
<h2
|
||||
*ngSwitchCase="'h2'"
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</h2>
|
||||
<h3
|
||||
*ngSwitchCase="'h3'"
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</h3>
|
||||
<h4
|
||||
*ngSwitchCase="'h4'"
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</h4>
|
||||
<span
|
||||
*ngSwitchDefault
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</span>
|
||||
</ng-container>
|
||||
`, isInline: true, dependencies: [{ kind: "ngmodule", type: NzTypographyModule }, { kind: "component", type: i1.NzTypographyComponent, selector: " nz-typography, [nz-typography], p[nz-paragraph], span[nz-text], h1[nz-title], h2[nz-title], h3[nz-title], h4[nz-title] ", inputs: ["nzCopyable", "nzEditable", "nzDisabled", "nzExpandable", "nzEllipsis", "nzCopyTooltips", "nzCopyIcons", "nzEditTooltip", "nzEditIcon", "nzContent", "nzEllipsisRows", "nzType", "nzCopyText", "nzSuffix"], outputs: ["nzContentChange", "nzCopy", "nzExpandChange", "nzOnEllipsis"], exportAs: ["nzTypography"] }, { kind: "directive", type: TachTypographyDirective, selector: "[tachTypography]", inputs: ["tachTypography", "tachTypographyVariant", "tachTypographyColor", "tachTypographyWeight", "tachTypographyClickable", "tachTypographyClassName", "tachTypographyEllipsis"] }, { kind: "directive", type: TachTypographyNzPropsDirective, selector: "[tachTypographyNzProps]", inputs: ["tachTypographyNzProps"] }, { kind: "directive", type: TachTypographyHostPropsDirective, selector: "[tachTypographyHostProps]", inputs: ["tachTypographyHostProps"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyComponent, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
selector: "tach-typography",
|
||||
standalone: true,
|
||||
imports: [
|
||||
NzTypographyModule,
|
||||
TachTypographyDirective,
|
||||
TachTypographyNzPropsDirective,
|
||||
TachTypographyHostPropsDirective,
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
NgSwitchDefault,
|
||||
NgStyle,
|
||||
NgTemplateOutlet,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<ng-template #tachTypographyProjectedContent>
|
||||
<ng-content></ng-content>
|
||||
</ng-template>
|
||||
<ng-template #tachTypographyMarkdownContent>
|
||||
<span [innerHTML]="renderedMarkdown"></span>
|
||||
</ng-template>
|
||||
|
||||
<ng-container [ngSwitch]="hostTag">
|
||||
<p
|
||||
*ngSwitchCase="'p'"
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</p>
|
||||
<a
|
||||
*ngSwitchCase="'a'"
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</a>
|
||||
<h1
|
||||
*ngSwitchCase="'h1'"
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</h1>
|
||||
<h2
|
||||
*ngSwitchCase="'h2'"
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</h2>
|
||||
<h3
|
||||
*ngSwitchCase="'h3'"
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</h3>
|
||||
<h4
|
||||
*ngSwitchCase="'h4'"
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</h4>
|
||||
<span
|
||||
*ngSwitchDefault
|
||||
nz-typography
|
||||
[tachTypography]="variant"
|
||||
[tachTypographyColor]="color"
|
||||
[tachTypographyWeight]="weight"
|
||||
[tachTypographyClickable]="clickable"
|
||||
[tachTypographyClassName]="className"
|
||||
[tachTypographyEllipsis]="ellipsis"
|
||||
[tachTypographyNzProps]="nzProps"
|
||||
[tachTypographyHostProps]="hostProps"
|
||||
[ngStyle]="preserveStyle"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="markdownEnabled ? tachTypographyMarkdownContent : tachTypographyProjectedContent"
|
||||
></ng-container>
|
||||
</span>
|
||||
</ng-container>
|
||||
`,
|
||||
}]
|
||||
}], propDecorators: { hostTag: [{
|
||||
type: Input,
|
||||
args: ["as"]
|
||||
}], variant: [{
|
||||
type: Input
|
||||
}], color: [{
|
||||
type: Input
|
||||
}], weight: [{
|
||||
type: Input
|
||||
}], clickable: [{
|
||||
type: Input
|
||||
}], markdownEnabled: [{
|
||||
type: Input
|
||||
}], markdown: [{
|
||||
type: Input
|
||||
}], className: [{
|
||||
type: Input
|
||||
}], ellipsis: [{
|
||||
type: Input
|
||||
}], nzProps: [{
|
||||
type: Input
|
||||
}], hostProps: [{
|
||||
type: Input
|
||||
}], preserveStyle: [{
|
||||
type: Input
|
||||
}], tachClick: [{
|
||||
type: Output
|
||||
}] } });
|
||||
export class TachTypographyNzModule {
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyNzModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
||||
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyNzModule, imports: [NzTypographyModule, TachTypographyDirective, TachTypographyNzPropsDirective, TachTypographyHostPropsDirective, TachTypographyComponent], exports: [NzTypographyModule, TachTypographyDirective, TachTypographyNzPropsDirective, TachTypographyHostPropsDirective, TachTypographyComponent] });
|
||||
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyNzModule, imports: [NzTypographyModule,
|
||||
TachTypographyComponent, NzTypographyModule] });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyNzModule, decorators: [{
|
||||
type: NgModule,
|
||||
args: [{
|
||||
imports: [
|
||||
NzTypographyModule,
|
||||
TachTypographyDirective,
|
||||
TachTypographyNzPropsDirective,
|
||||
TachTypographyHostPropsDirective,
|
||||
TachTypographyComponent,
|
||||
],
|
||||
exports: [
|
||||
NzTypographyModule,
|
||||
TachTypographyDirective,
|
||||
TachTypographyNzPropsDirective,
|
||||
TachTypographyHostPropsDirective,
|
||||
TachTypographyComponent,
|
||||
],
|
||||
}]
|
||||
}] });
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/tach-typography/dist/angular/angular/index.js.map
vendored
Normal file
1
packages/tach-typography/dist/angular/angular/index.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
packages/tach-typography/dist/angular/angular/public-api.d.ts
vendored
Normal file
2
packages/tach-typography/dist/angular/angular/public-api.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./index";
|
||||
//# sourceMappingURL=public-api.d.ts.map
|
||||
1
packages/tach-typography/dist/angular/angular/public-api.d.ts.map
vendored
Normal file
1
packages/tach-typography/dist/angular/angular/public-api.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../../../src/angular/public-api.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC"}
|
||||
2
packages/tach-typography/dist/angular/angular/public-api.js
vendored
Normal file
2
packages/tach-typography/dist/angular/angular/public-api.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./index";
|
||||
//# sourceMappingURL=public-api.js.map
|
||||
1
packages/tach-typography/dist/angular/angular/public-api.js.map
vendored
Normal file
1
packages/tach-typography/dist/angular/angular/public-api.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../../src/angular/public-api.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC"}
|
||||
4
packages/tach-typography/dist/angular/core/classnames.d.ts
vendored
Normal file
4
packages/tach-typography/dist/angular/core/classnames.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import type { TypographyClassOptions } from "./types";
|
||||
export declare const tachTypographyClassName: ({ variant, color, weight, clickable, className, }?: TypographyClassOptions) => string;
|
||||
export declare const tachTypographyClassList: (options?: TypographyClassOptions) => string[];
|
||||
//# sourceMappingURL=classnames.d.ts.map
|
||||
1
packages/tach-typography/dist/angular/core/classnames.d.ts.map
vendored
Normal file
1
packages/tach-typography/dist/angular/core/classnames.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"classnames.d.ts","sourceRoot":"","sources":["../../../src/core/classnames.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAOtD,eAAO,MAAM,uBAAuB,GAAI,oDAMrC,sBAA2B,KAAG,MAShC,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,UAAS,sBAA2B,KAAG,MAAM,EAIpF,CAAC"}
|
||||
11
packages/tach-typography/dist/angular/core/classnames.js
vendored
Normal file
11
packages/tach-typography/dist/angular/core/classnames.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const BASE_CLASS = "tach-typography";
|
||||
const join = (...parts) => parts.filter(Boolean).join(" ");
|
||||
export const tachTypographyClassName = ({ variant = "Body", color = "primary", weight = "normal", clickable = false, className, } = {}) => {
|
||||
return join(BASE_CLASS, `${BASE_CLASS}--${variant}`, `${BASE_CLASS}--color-${color}`, weight === "bold" && `${BASE_CLASS}--bold`, clickable && `${BASE_CLASS}--pointer`, className);
|
||||
};
|
||||
export const tachTypographyClassList = (options = {}) => {
|
||||
return tachTypographyClassName(options)
|
||||
.split(" ")
|
||||
.filter(Boolean);
|
||||
};
|
||||
//# sourceMappingURL=classnames.js.map
|
||||
1
packages/tach-typography/dist/angular/core/classnames.js.map
vendored
Normal file
1
packages/tach-typography/dist/angular/core/classnames.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"classnames.js","sourceRoot":"","sources":["../../../src/core/classnames.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAAG,iBAAiB,CAAC;AAErC,MAAM,IAAI,GAAG,CAAC,GAAG,KAA+C,EAAU,EAAE,CAC1E,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAElC,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,EACtC,OAAO,GAAG,MAAM,EAChB,KAAK,GAAG,SAAS,EACjB,MAAM,GAAG,QAAQ,EACjB,SAAS,GAAG,KAAK,EACjB,SAAS,MACiB,EAAE,EAAU,EAAE;IACxC,OAAO,IAAI,CACT,UAAU,EACV,GAAG,UAAU,KAAK,OAAO,EAAE,EAC3B,GAAG,UAAU,WAAW,KAAK,EAAE,EAC/B,MAAM,KAAK,MAAM,IAAI,GAAG,UAAU,QAAQ,EAC1C,SAAS,IAAI,GAAG,UAAU,WAAW,EACrC,SAAS,CACV,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,UAAkC,EAAE,EAAY,EAAE;IACxF,OAAO,uBAAuB,CAAC,OAAO,CAAC;SACpC,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC,CAAC"}
|
||||
5
packages/tach-typography/dist/angular/core/ellipsis.d.ts
vendored
Normal file
5
packages/tach-typography/dist/angular/core/ellipsis.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import type { EllipsisOptions } from "./types";
|
||||
type StyleRecord = Record<string, string | number>;
|
||||
export declare const tachTypographyEllipsisStyle: (ellipsis?: EllipsisOptions) => StyleRecord | undefined;
|
||||
export {};
|
||||
//# sourceMappingURL=ellipsis.d.ts.map
|
||||
1
packages/tach-typography/dist/angular/core/ellipsis.d.ts.map
vendored
Normal file
1
packages/tach-typography/dist/angular/core/ellipsis.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ellipsis.d.ts","sourceRoot":"","sources":["../../../src/core/ellipsis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AAEnD,eAAO,MAAM,2BAA2B,GACtC,WAAW,eAAe,KACzB,WAAW,GAAG,SAchB,CAAC"}
|
||||
14
packages/tach-typography/dist/angular/core/ellipsis.js
vendored
Normal file
14
packages/tach-typography/dist/angular/core/ellipsis.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
export const tachTypographyEllipsisStyle = (ellipsis) => {
|
||||
if (!ellipsis) {
|
||||
return undefined;
|
||||
}
|
||||
const rows = typeof ellipsis === "object" ? ellipsis.rows ?? 1 : 1;
|
||||
return {
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
display: "-webkit-box",
|
||||
WebkitBoxOrient: "vertical",
|
||||
WebkitLineClamp: rows,
|
||||
};
|
||||
};
|
||||
//# sourceMappingURL=ellipsis.js.map
|
||||
1
packages/tach-typography/dist/angular/core/ellipsis.js.map
vendored
Normal file
1
packages/tach-typography/dist/angular/core/ellipsis.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ellipsis.js","sourceRoot":"","sources":["../../../src/core/ellipsis.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,QAA0B,EACD,EAAE;IAC3B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEnE,OAAO;QACL,QAAQ,EAAE,QAAQ;QAClB,YAAY,EAAE,UAAU;QACxB,OAAO,EAAE,aAAa;QACtB,eAAe,EAAE,UAAU;QAC3B,eAAe,EAAE,IAAI;KACtB,CAAC;AACJ,CAAC,CAAC"}
|
||||
5
packages/tach-typography/dist/angular/core/index.d.ts
vendored
Normal file
5
packages/tach-typography/dist/angular/core/index.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from "./types";
|
||||
export * from "./classnames";
|
||||
export * from "./ellipsis";
|
||||
export * from "./markdown";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
packages/tach-typography/dist/angular/core/index.d.ts.map
vendored
Normal file
1
packages/tach-typography/dist/angular/core/index.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
|
||||
5
packages/tach-typography/dist/angular/core/index.js
vendored
Normal file
5
packages/tach-typography/dist/angular/core/index.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from "./types";
|
||||
export * from "./classnames";
|
||||
export * from "./ellipsis";
|
||||
export * from "./markdown";
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/tach-typography/dist/angular/core/index.js.map
vendored
Normal file
1
packages/tach-typography/dist/angular/core/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
|
||||
2
packages/tach-typography/dist/angular/core/markdown.d.ts
vendored
Normal file
2
packages/tach-typography/dist/angular/core/markdown.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export declare const tachTypographyMarkdownToHtml: (markdown: string) => string;
|
||||
//# sourceMappingURL=markdown.d.ts.map
|
||||
1
packages/tach-typography/dist/angular/core/markdown.d.ts.map
vendored
Normal file
1
packages/tach-typography/dist/angular/core/markdown.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../src/core/markdown.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,4BAA4B,GAAI,UAAU,MAAM,KAAG,MAyC/D,CAAC"}
|
||||
47
packages/tach-typography/dist/angular/core/markdown.js
vendored
Normal file
47
packages/tach-typography/dist/angular/core/markdown.js
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
const TOKEN_PREFIX = "TACHMDTOKEN";
|
||||
const SAFE_HREF_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
|
||||
const escapeHtml = (value) => value
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
const normalizeMarkdown = (value) => value.replace(/\r\n?/g, "\n");
|
||||
const sanitizeHref = (value) => {
|
||||
const href = value.trim();
|
||||
if (!href || !SAFE_HREF_PATTERN.test(href)) {
|
||||
return null;
|
||||
}
|
||||
return href;
|
||||
};
|
||||
export const tachTypographyMarkdownToHtml = (markdown) => {
|
||||
const source = normalizeMarkdown(markdown);
|
||||
const tokenMap = new Map();
|
||||
let tokenId = 0;
|
||||
const tokenized = source
|
||||
.replace(/`([^`\n]+)`/g, (_match, code) => {
|
||||
const token = `${TOKEN_PREFIX}${tokenId++}`;
|
||||
tokenMap.set(token, `<code class="tach-typography__md-code">${escapeHtml(code)}</code>`);
|
||||
return token;
|
||||
})
|
||||
.replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
|
||||
const token = `${TOKEN_PREFIX}${tokenId++}`;
|
||||
const href = sanitizeHref(hrefRaw);
|
||||
if (!href) {
|
||||
tokenMap.set(token, escapeHtml(label));
|
||||
return token;
|
||||
}
|
||||
tokenMap.set(token, `<a class="tach-typography__md-link" href="${escapeHtml(href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)}</a>`);
|
||||
return token;
|
||||
});
|
||||
let html = escapeHtml(tokenized)
|
||||
.replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>")
|
||||
.replace(/__([^_\n]+)__/g, "<strong>$1</strong>")
|
||||
.replace(/\*([^*\n]+)\*/g, "<em>$1</em>")
|
||||
.replace(/_([^_\n]+)_/g, "<em>$1</em>")
|
||||
.replace(/~~([^~\n]+)~~/g, "<del>$1</del>")
|
||||
.replace(/\n/g, "<br />");
|
||||
html = html.replace(new RegExp(`${TOKEN_PREFIX}\\d+`, "g"), token => tokenMap.get(token) || token);
|
||||
return html;
|
||||
};
|
||||
//# sourceMappingURL=markdown.js.map
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user