release(tach-typography): v0.3.0

This commit is contained in:
2026-03-31 18:36:20 +03:00
parent a8c2eaa2fd
commit cacbc016ec
35 changed files with 799 additions and 85 deletions

View File

@@ -58,6 +58,14 @@ export const Example = () => (
); );
``` ```
Markdown as a prop (keeps existing API):
```tsx
<TachTypography.Text.AccentH1 markdownEnabled>
**Bold** _italic_ [Docs](https://example.com)
</TachTypography.Text.AccentH1>
```
## Angular usage (NG-ZORRO) ## Angular usage (NG-ZORRO)
```ts ```ts
@@ -92,6 +100,12 @@ import { TachTypographyComponent } from "@hublib-web/tach-typography/angular";
export class ExampleComponent {} export class ExampleComponent {}
``` ```
Markdown in Angular:
```html
<tach-typography variant="Body" [markdownEnabled]="true" [markdown]="'**Bold** _italic_'"></tach-typography>
```
## Storybook (dev/design system) ## Storybook (dev/design system)
Run from repository root: Run from repository root:

View File

@@ -56,21 +56,25 @@ export declare class TachTypographyHostPropsDirective implements OnChanges {
static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyHostPropsDirective, never>; static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyHostPropsDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<TachTypographyHostPropsDirective, "[tachTypographyHostProps]", never, { "tachTypographyHostProps": { "alias": "tachTypographyHostProps"; "required": false; }; }, {}, never, never, true, never>; static ɵdir: i0.ɵɵDirectiveDeclaration<TachTypographyHostPropsDirective, "[tachTypographyHostProps]", never, { "tachTypographyHostProps": { "alias": "tachTypographyHostProps"; "required": false; }; }, {}, never, never, true, never>;
} }
export declare class TachTypographyComponent { export declare class TachTypographyComponent implements OnChanges {
hostTag: TachTypographyHostTag; hostTag: TachTypographyHostTag;
variant: TypographyVariant; variant: TypographyVariant;
color: TypographyColor; color: TypographyColor;
weight: TypographyWeight; weight: TypographyWeight;
clickable: boolean; clickable: boolean;
markdownEnabled: boolean;
markdown: string | undefined;
className: string | undefined; className: string | undefined;
ellipsis: EllipsisOptions | undefined; ellipsis: EllipsisOptions | undefined;
nzProps: TachTypographyNzProps | undefined; nzProps: TachTypographyNzProps | undefined;
hostProps: TachTypographyHostProps | undefined; hostProps: TachTypographyHostProps | undefined;
preserveStyle: Record<string, string | number> | undefined; preserveStyle: Record<string, string | number> | undefined;
readonly tachClick: EventEmitter<MouseEvent>; readonly tachClick: EventEmitter<MouseEvent>;
renderedMarkdown: string;
ngOnChanges(_changes: SimpleChanges): void;
handleClick(event: MouseEvent): void; handleClick(event: MouseEvent): void;
static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyComponent, never>; static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<TachTypographyComponent, "tach-typography", never, { "hostTag": { "alias": "as"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "color": { "alias": "color"; "required": false; }; "weight": { "alias": "weight"; "required": false; }; "clickable": { "alias": "clickable"; "required": false; }; "className": { "alias": "className"; "required": false; }; "ellipsis": { "alias": "ellipsis"; "required": false; }; "nzProps": { "alias": "nzProps"; "required": false; }; "hostProps": { "alias": "hostProps"; "required": false; }; "preserveStyle": { "alias": "preserveStyle"; "required": false; }; }, { "tachClick": "tachClick"; }, never, ["*", "*", "*", "*", "*", "*", "*"], true, never>; static ɵcmp: i0.ɵɵComponentDeclaration<TachTypographyComponent, "tach-typography", never, { "hostTag": { "alias": "as"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "color": { "alias": "color"; "required": false; }; "weight": { "alias": "weight"; "required": false; }; "clickable": { "alias": "clickable"; "required": false; }; "markdownEnabled": { "alias": "markdownEnabled"; "required": false; }; "markdown": { "alias": "markdown"; "required": false; }; "className": { "alias": "className"; "required": false; }; "ellipsis": { "alias": "ellipsis"; "required": false; }; "nzProps": { "alias": "nzProps"; "required": false; }; "hostProps": { "alias": "hostProps"; "required": false; }; "preserveStyle": { "alias": "preserveStyle"; "required": false; }; }, { "tachClick": "tachClick"; }, never, ["*", "*", "*", "*", "*", "*", "*"], true, never>;
} }
export declare class TachTypographyNzModule { export declare class TachTypographyNzModule {
static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyNzModule, never>; static ɵfac: i0.ɵɵFactoryDeclaration<TachTypographyNzModule, never>;

View File

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

View File

@@ -1,7 +1,7 @@
import { NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault } from "@angular/common"; import { NgIf, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault } from "@angular/common";
import { ChangeDetectionStrategy, Component, Directive, EventEmitter, inject, Input, NgModule, Output, } from "@angular/core"; import { ChangeDetectionStrategy, Component, Directive, EventEmitter, inject, Input, NgModule, Output, } from "@angular/core";
import { NzTypographyComponent, NzTypographyModule } from "ng-zorro-antd/typography"; import { NzTypographyComponent, NzTypographyModule } from "ng-zorro-antd/typography";
import { tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle, } from "../core"; import { tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle, tachTypographyMarkdownToHtml, } from "../core";
import * as i0 from "@angular/core"; import * as i0 from "@angular/core";
import * as i1 from "ng-zorro-antd/typography"; import * as i1 from "ng-zorro-antd/typography";
const camelToKebab = (value) => value.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`); const camelToKebab = (value) => value.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
@@ -233,17 +233,25 @@ export class TachTypographyComponent {
color = "primary"; color = "primary";
weight = "normal"; weight = "normal";
clickable = false; clickable = false;
markdownEnabled = false;
markdown;
className; className;
ellipsis; ellipsis;
nzProps; nzProps;
hostProps; hostProps;
preserveStyle; preserveStyle;
tachClick = new EventEmitter(); tachClick = new EventEmitter();
renderedMarkdown = "";
ngOnChanges(_changes) {
this.renderedMarkdown = this.markdownEnabled
? tachTypographyMarkdownToHtml(this.markdown ?? "")
: "";
}
handleClick(event) { handleClick(event) {
this.tachClick.emit(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 ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: TachTypographyComponent, isStandalone: true, selector: "tach-typography", inputs: { hostTag: ["as", "hostTag"], variant: "variant", color: "color", weight: "weight", clickable: "clickable", className: "className", ellipsis: "ellipsis", nzProps: "nzProps", hostProps: "hostProps", preserveStyle: "preserveStyle" }, outputs: { tachClick: "tachClick" }, ngImport: i0, template: ` static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.18", type: TachTypographyComponent, isStandalone: true, selector: "tach-typography", inputs: { hostTag: ["as", "hostTag"], variant: "variant", color: "color", weight: "weight", clickable: "clickable", markdownEnabled: "markdownEnabled", markdown: "markdown", className: "className", ellipsis: "ellipsis", nzProps: "nzProps", hostProps: "hostProps", preserveStyle: "preserveStyle" }, outputs: { tachClick: "tachClick" }, usesOnChanges: true, ngImport: i0, template: `
<ng-container [ngSwitch]="hostTag"> <ng-container [ngSwitch]="hostTag">
<p <p
*ngSwitchCase="'p'" *ngSwitchCase="'p'"
@@ -259,7 +267,12 @@ export class TachTypographyComponent {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentP">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentP>
<ng-content /> <ng-content />
</ng-template>
</p> </p>
<a <a
*ngSwitchCase="'a'" *ngSwitchCase="'a'"
@@ -275,7 +288,12 @@ export class TachTypographyComponent {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentA">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentA>
<ng-content /> <ng-content />
</ng-template>
</a> </a>
<h1 <h1
*ngSwitchCase="'h1'" *ngSwitchCase="'h1'"
@@ -291,7 +309,12 @@ export class TachTypographyComponent {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH1">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentH1>
<ng-content /> <ng-content />
</ng-template>
</h1> </h1>
<h2 <h2
*ngSwitchCase="'h2'" *ngSwitchCase="'h2'"
@@ -307,7 +330,12 @@ export class TachTypographyComponent {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH2">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentH2>
<ng-content /> <ng-content />
</ng-template>
</h2> </h2>
<h3 <h3
*ngSwitchCase="'h3'" *ngSwitchCase="'h3'"
@@ -323,7 +351,12 @@ export class TachTypographyComponent {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH3">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentH3>
<ng-content /> <ng-content />
</ng-template>
</h3> </h3>
<h4 <h4
*ngSwitchCase="'h4'" *ngSwitchCase="'h4'"
@@ -339,7 +372,12 @@ export class TachTypographyComponent {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH4">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentH4>
<ng-content /> <ng-content />
</ng-template>
</h4> </h4>
<span <span
*ngSwitchDefault *ngSwitchDefault
@@ -355,10 +393,15 @@ export class TachTypographyComponent {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentSpan">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentSpan>
<ng-content /> <ng-content />
</ng-template>
</span> </span>
</ng-container> </ng-container>
`, isInline: true, dependencies: [{ kind: "ngmodule", type: NzTypographyModule }, { kind: "component", type: i1.NzTypographyComponent, selector: " nz-typography, [nz-typography], p[nz-paragraph], span[nz-text], h1[nz-title], h2[nz-title], h3[nz-title], h4[nz-title] ", inputs: ["nzCopyable", "nzEditable", "nzDisabled", "nzExpandable", "nzEllipsis", "nzCopyTooltips", "nzCopyIcons", "nzEditTooltip", "nzEditIcon", "nzContent", "nzEllipsisRows", "nzType", "nzCopyText", "nzSuffix"], outputs: ["nzContentChange", "nzCopy", "nzExpandChange", "nzOnEllipsis"], exportAs: ["nzTypography"] }, { kind: "directive", type: TachTypographyDirective, selector: "[tachTypography]", inputs: ["tachTypography", "tachTypographyVariant", "tachTypographyColor", "tachTypographyWeight", "tachTypographyClickable", "tachTypographyClassName", "tachTypographyEllipsis"] }, { kind: "directive", type: TachTypographyNzPropsDirective, selector: "[tachTypographyNzProps]", inputs: ["tachTypographyNzProps"] }, { kind: "directive", type: TachTypographyHostPropsDirective, selector: "[tachTypographyHostProps]", inputs: ["tachTypographyHostProps"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); `, isInline: true, dependencies: [{ kind: "ngmodule", type: NzTypographyModule }, { kind: "component", type: i1.NzTypographyComponent, selector: " nz-typography, [nz-typography], p[nz-paragraph], span[nz-text], h1[nz-title], h2[nz-title], h3[nz-title], h4[nz-title] ", inputs: ["nzCopyable", "nzEditable", "nzDisabled", "nzExpandable", "nzEllipsis", "nzCopyTooltips", "nzCopyIcons", "nzEditTooltip", "nzEditIcon", "nzContent", "nzEllipsisRows", "nzType", "nzCopyText", "nzSuffix"], outputs: ["nzContentChange", "nzCopy", "nzExpandChange", "nzOnEllipsis"], exportAs: ["nzTypography"] }, { kind: "directive", type: TachTypographyDirective, selector: "[tachTypography]", inputs: ["tachTypography", "tachTypographyVariant", "tachTypographyColor", "tachTypographyWeight", "tachTypographyClickable", "tachTypographyClassName", "tachTypographyEllipsis"] }, { kind: "directive", type: TachTypographyNzPropsDirective, selector: "[tachTypographyNzProps]", inputs: ["tachTypographyNzProps"] }, { kind: "directive", type: TachTypographyHostPropsDirective, selector: "[tachTypographyHostProps]", inputs: ["tachTypographyHostProps"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
} }
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyComponent, decorators: [{ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: TachTypographyComponent, decorators: [{
type: Component, type: Component,
@@ -373,6 +416,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
NgSwitch, NgSwitch,
NgSwitchCase, NgSwitchCase,
NgSwitchDefault, NgSwitchDefault,
NgIf,
NgStyle, NgStyle,
], ],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
@@ -392,7 +436,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentP">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentP>
<ng-content /> <ng-content />
</ng-template>
</p> </p>
<a <a
*ngSwitchCase="'a'" *ngSwitchCase="'a'"
@@ -408,7 +457,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentA">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentA>
<ng-content /> <ng-content />
</ng-template>
</a> </a>
<h1 <h1
*ngSwitchCase="'h1'" *ngSwitchCase="'h1'"
@@ -424,7 +478,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH1">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentH1>
<ng-content /> <ng-content />
</ng-template>
</h1> </h1>
<h2 <h2
*ngSwitchCase="'h2'" *ngSwitchCase="'h2'"
@@ -440,7 +499,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH2">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentH2>
<ng-content /> <ng-content />
</ng-template>
</h2> </h2>
<h3 <h3
*ngSwitchCase="'h3'" *ngSwitchCase="'h3'"
@@ -456,7 +520,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH3">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentH3>
<ng-content /> <ng-content />
</ng-template>
</h3> </h3>
<h4 <h4
*ngSwitchCase="'h4'" *ngSwitchCase="'h4'"
@@ -472,7 +541,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH4">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentH4>
<ng-content /> <ng-content />
</ng-template>
</h4> </h4>
<span <span
*ngSwitchDefault *ngSwitchDefault
@@ -488,7 +562,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentSpan">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentSpan>
<ng-content /> <ng-content />
</ng-template>
</span> </span>
</ng-container> </ng-container>
`, `,
@@ -504,6 +583,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
type: Input type: Input
}], clickable: [{ }], clickable: [{
type: Input type: Input
}], markdownEnabled: [{
type: Input
}], markdown: [{
type: Input
}], className: [{ }], className: [{
type: Input type: Input
}], ellipsis: [{ }], ellipsis: [{

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,5 @@
export * from "./types"; export * from "./types";
export * from "./classnames"; export * from "./classnames";
export * from "./ellipsis"; export * from "./ellipsis";
export * from "./markdown";
//# sourceMappingURL=index.d.ts.map //# sourceMappingURL=index.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"} {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}

View File

@@ -1,4 +1,5 @@
export * from "./types"; export * from "./types";
export * from "./classnames"; export * from "./classnames";
export * from "./ellipsis"; export * from "./ellipsis";
export * from "./markdown";
//# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"} {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}

View File

@@ -0,0 +1,2 @@
export declare const tachTypographyMarkdownToHtml: (markdown: string) => string;
//# sourceMappingURL=markdown.d.ts.map

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

View File

@@ -0,0 +1,47 @@
const TOKEN_PREFIX = "TACHMDTOKEN";
const SAFE_HREF_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
const escapeHtml = (value) => value
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
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

View File

@@ -0,0 +1 @@
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../../src/core/markdown.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG,aAAa,CAAC;AACnC,MAAM,iBAAiB,GAAG,+BAA+B,CAAC;AAE1D,MAAM,UAAU,GAAG,CAAC,KAAa,EAAU,EAAE,CAC3C,KAAK;KACF,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;KACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;KACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;KACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;KACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAE5B,MAAM,iBAAiB,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AAEnF,MAAM,YAAY,GAAG,CAAC,KAAa,EAAiB,EAAE;IACpD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAE1B,IAAI,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,QAAgB,EAAU,EAAE;IACvE,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,MAAM,SAAS,GAAG,MAAM;SACrB,OAAO,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE;QAChD,MAAM,KAAK,GAAG,GAAG,YAAY,GAAG,OAAO,EAAE,EAAE,CAAC;QAC5C,QAAQ,CAAC,GAAG,CACV,KAAK,EACL,0CAA0C,UAAU,CAAC,IAAI,CAAC,SAAS,CACpE,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;SACD,OAAO,CAAC,8BAA8B,EAAE,CAAC,MAAM,EAAE,KAAa,EAAE,OAAe,EAAE,EAAE;QAClF,MAAM,KAAK,GAAG,GAAG,YAAY,GAAG,OAAO,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QAEnC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;YACvC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,QAAQ,CAAC,GAAG,CACV,KAAK,EACL,6CAA6C,UAAU,CAAC,IAAI,CAAC,+CAA+C,UAAU,CAAC,KAAK,CAAC,MAAM,CACpI,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IAEL,IAAI,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC;SAC7B,OAAO,CAAC,oBAAoB,EAAE,qBAAqB,CAAC;SACpD,OAAO,CAAC,gBAAgB,EAAE,qBAAqB,CAAC;SAChD,OAAO,CAAC,gBAAgB,EAAE,aAAa,CAAC;SACxC,OAAO,CAAC,cAAc,EAAE,aAAa,CAAC;SACtC,OAAO,CAAC,gBAAgB,EAAE,eAAe,CAAC;SAC1C,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAE5B,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,GAAG,YAAY,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC;IAEnG,OAAO,IAAI,CAAC;AACd,CAAC,CAAC"}

View File

@@ -78,10 +78,52 @@ var tachTypographyEllipsisStyle = (ellipsis) => {
}; };
}; };
// src/core/markdown.ts
var TOKEN_PREFIX = "TACHMDTOKEN";
var SAFE_HREF_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
var escapeHtml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
var normalizeMarkdown = (value) => value.replace(/\r\n?/g, "\n");
var sanitizeHref = (value) => {
const href = value.trim();
if (!href || !SAFE_HREF_PATTERN.test(href)) {
return null;
}
return href;
};
var tachTypographyMarkdownToHtml = (markdown) => {
const source = normalizeMarkdown(markdown);
const tokenMap = /* @__PURE__ */ new Map();
let tokenId = 0;
const tokenized = source.replace(/`([^`\n]+)`/g, (_match, code) => {
const token = `${TOKEN_PREFIX}${tokenId++}`;
tokenMap.set(
token,
`<code class="tach-typography__md-code">${escapeHtml(code)}</code>`
);
return token;
}).replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
const token = `${TOKEN_PREFIX}${tokenId++}`;
const href = sanitizeHref(hrefRaw);
if (!href) {
tokenMap.set(token, escapeHtml(label));
return token;
}
tokenMap.set(
token,
`<a class="tach-typography__md-link" href="${escapeHtml(href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)}</a>`
);
return token;
});
let html = escapeHtml(tokenized).replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>").replace(/__([^_\n]+)__/g, "<strong>$1</strong>").replace(/\*([^*\n]+)\*/g, "<em>$1</em>").replace(/_([^_\n]+)_/g, "<em>$1</em>").replace(/~~([^~\n]+)~~/g, "<del>$1</del>").replace(/\n/g, "<br />");
html = html.replace(new RegExp(`${TOKEN_PREFIX}\\d+`, "g"), (token) => tokenMap.get(token) || token);
return html;
};
exports.TYPOGRAPHY_COLORS = TYPOGRAPHY_COLORS; exports.TYPOGRAPHY_COLORS = TYPOGRAPHY_COLORS;
exports.TYPOGRAPHY_VARIANTS = TYPOGRAPHY_VARIANTS; exports.TYPOGRAPHY_VARIANTS = TYPOGRAPHY_VARIANTS;
exports.tachTypographyClassList = tachTypographyClassList; exports.tachTypographyClassList = tachTypographyClassList;
exports.tachTypographyClassName = tachTypographyClassName; exports.tachTypographyClassName = tachTypographyClassName;
exports.tachTypographyEllipsisStyle = tachTypographyEllipsisStyle; exports.tachTypographyEllipsisStyle = tachTypographyEllipsisStyle;
exports.tachTypographyMarkdownToHtml = tachTypographyMarkdownToHtml;
//# sourceMappingURL=index.cjs.map //# sourceMappingURL=index.cjs.map
//# sourceMappingURL=index.cjs.map //# sourceMappingURL=index.cjs.map

File diff suppressed because one or more lines are too long

View File

@@ -7,4 +7,6 @@ declare const tachTypographyClassList: (options?: TypographyClassOptions) => str
type StyleRecord = Record<string, string | number>; type StyleRecord = Record<string, string | number>;
declare const tachTypographyEllipsisStyle: (ellipsis?: EllipsisOptions) => StyleRecord | undefined; declare const tachTypographyEllipsisStyle: (ellipsis?: EllipsisOptions) => StyleRecord | undefined;
export { EllipsisOptions, TypographyClassOptions, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle }; declare const tachTypographyMarkdownToHtml: (markdown: string) => string;
export { EllipsisOptions, TypographyClassOptions, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle, tachTypographyMarkdownToHtml };

View File

@@ -7,4 +7,6 @@ declare const tachTypographyClassList: (options?: TypographyClassOptions) => str
type StyleRecord = Record<string, string | number>; type StyleRecord = Record<string, string | number>;
declare const tachTypographyEllipsisStyle: (ellipsis?: EllipsisOptions) => StyleRecord | undefined; declare const tachTypographyEllipsisStyle: (ellipsis?: EllipsisOptions) => StyleRecord | undefined;
export { EllipsisOptions, TypographyClassOptions, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle }; declare const tachTypographyMarkdownToHtml: (markdown: string) => string;
export { EllipsisOptions, TypographyClassOptions, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle, tachTypographyMarkdownToHtml };

View File

@@ -76,6 +76,47 @@ var tachTypographyEllipsisStyle = (ellipsis) => {
}; };
}; };
export { TYPOGRAPHY_COLORS, TYPOGRAPHY_VARIANTS, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle }; // src/core/markdown.ts
var TOKEN_PREFIX = "TACHMDTOKEN";
var SAFE_HREF_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
var escapeHtml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
var normalizeMarkdown = (value) => value.replace(/\r\n?/g, "\n");
var sanitizeHref = (value) => {
const href = value.trim();
if (!href || !SAFE_HREF_PATTERN.test(href)) {
return null;
}
return href;
};
var tachTypographyMarkdownToHtml = (markdown) => {
const source = normalizeMarkdown(markdown);
const tokenMap = /* @__PURE__ */ new Map();
let tokenId = 0;
const tokenized = source.replace(/`([^`\n]+)`/g, (_match, code) => {
const token = `${TOKEN_PREFIX}${tokenId++}`;
tokenMap.set(
token,
`<code class="tach-typography__md-code">${escapeHtml(code)}</code>`
);
return token;
}).replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
const token = `${TOKEN_PREFIX}${tokenId++}`;
const href = sanitizeHref(hrefRaw);
if (!href) {
tokenMap.set(token, escapeHtml(label));
return token;
}
tokenMap.set(
token,
`<a class="tach-typography__md-link" href="${escapeHtml(href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)}</a>`
);
return token;
});
let html = escapeHtml(tokenized).replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>").replace(/__([^_\n]+)__/g, "<strong>$1</strong>").replace(/\*([^*\n]+)\*/g, "<em>$1</em>").replace(/_([^_\n]+)_/g, "<em>$1</em>").replace(/~~([^~\n]+)~~/g, "<del>$1</del>").replace(/\n/g, "<br />");
html = html.replace(new RegExp(`${TOKEN_PREFIX}\\d+`, "g"), (token) => tokenMap.get(token) || token);
return html;
};
export { TYPOGRAPHY_COLORS, TYPOGRAPHY_VARIANTS, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle, tachTypographyMarkdownToHtml };
//# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -29,9 +29,62 @@ var tachTypographyClassName = ({
className className
); );
}; };
// src/core/markdown.ts
var TOKEN_PREFIX = "TACHMDTOKEN";
var SAFE_HREF_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
var escapeHtml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
var normalizeMarkdown = (value) => value.replace(/\r\n?/g, "\n");
var sanitizeHref = (value) => {
const href = value.trim();
if (!href || !SAFE_HREF_PATTERN.test(href)) {
return null;
}
return href;
};
var tachTypographyMarkdownToHtml = (markdown) => {
const source = normalizeMarkdown(markdown);
const tokenMap = /* @__PURE__ */ new Map();
let tokenId = 0;
const tokenized = source.replace(/`([^`\n]+)`/g, (_match, code) => {
const token = `${TOKEN_PREFIX}${tokenId++}`;
tokenMap.set(
token,
`<code class="tach-typography__md-code">${escapeHtml(code)}</code>`
);
return token;
}).replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
const token = `${TOKEN_PREFIX}${tokenId++}`;
const href = sanitizeHref(hrefRaw);
if (!href) {
tokenMap.set(token, escapeHtml(label));
return token;
}
tokenMap.set(
token,
`<a class="tach-typography__md-link" href="${escapeHtml(href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)}</a>`
);
return token;
});
let html = escapeHtml(tokenized).replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>").replace(/__([^_\n]+)__/g, "<strong>$1</strong>").replace(/\*([^*\n]+)\*/g, "<em>$1</em>").replace(/_([^_\n]+)_/g, "<em>$1</em>").replace(/~~([^~\n]+)~~/g, "<del>$1</del>").replace(/\n/g, "<br />");
html = html.replace(new RegExp(`${TOKEN_PREFIX}\\d+`, "g"), (token) => tokenMap.get(token) || token);
return html;
};
var createTypographyVariant = (Component, variant) => { var createTypographyVariant = (Component, variant) => {
const Variant = React__default.default.forwardRef( const Variant = React__default.default.forwardRef(
({ color = "primary", weight = "normal", className, onClick, ...rest }, ref) => /* @__PURE__ */ jsxRuntime.jsx( ({
color = "primary",
weight = "normal",
className,
onClick,
markdownEnabled = false,
children,
...rest
}, ref) => {
const markdownHtml = markdownEnabled && typeof children === "string" ? tachTypographyMarkdownToHtml(children) : void 0;
const renderedChildren = markdownHtml ? /* @__PURE__ */ jsxRuntime.jsx("span", { dangerouslySetInnerHTML: { __html: markdownHtml } }) : children;
const contentProps = { children: renderedChildren };
return /* @__PURE__ */ jsxRuntime.jsx(
Component, Component,
{ {
ref, ref,
@@ -43,9 +96,11 @@ var createTypographyVariant = (Component, variant) => {
clickable: Boolean(onClick) clickable: Boolean(onClick)
}), }),
onClick, onClick,
...rest ...rest,
...contentProps
}
);
} }
)
); );
Variant.displayName = String(variant); Variant.displayName = String(variant);
return Variant; return Variant;

File diff suppressed because one or more lines are too long

View File

@@ -10,6 +10,8 @@ interface AdditionalProps {
weight?: TypographyWeight; weight?: TypographyWeight;
onClick?: (event: React.MouseEvent<HTMLElement>) => void; onClick?: (event: React.MouseEvent<HTMLElement>) => void;
className?: string | undefined; className?: string | undefined;
markdownEnabled?: boolean;
children?: React.ReactNode;
} }
declare const TachTypography: { declare const TachTypography: {
Text: { Text: {

View File

@@ -10,6 +10,8 @@ interface AdditionalProps {
weight?: TypographyWeight; weight?: TypographyWeight;
onClick?: (event: React.MouseEvent<HTMLElement>) => void; onClick?: (event: React.MouseEvent<HTMLElement>) => void;
className?: string | undefined; className?: string | undefined;
markdownEnabled?: boolean;
children?: React.ReactNode;
} }
declare const TachTypography: { declare const TachTypography: {
Text: { Text: {

View File

@@ -23,9 +23,62 @@ var tachTypographyClassName = ({
className className
); );
}; };
// src/core/markdown.ts
var TOKEN_PREFIX = "TACHMDTOKEN";
var SAFE_HREF_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
var escapeHtml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
var normalizeMarkdown = (value) => value.replace(/\r\n?/g, "\n");
var sanitizeHref = (value) => {
const href = value.trim();
if (!href || !SAFE_HREF_PATTERN.test(href)) {
return null;
}
return href;
};
var tachTypographyMarkdownToHtml = (markdown) => {
const source = normalizeMarkdown(markdown);
const tokenMap = /* @__PURE__ */ new Map();
let tokenId = 0;
const tokenized = source.replace(/`([^`\n]+)`/g, (_match, code) => {
const token = `${TOKEN_PREFIX}${tokenId++}`;
tokenMap.set(
token,
`<code class="tach-typography__md-code">${escapeHtml(code)}</code>`
);
return token;
}).replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
const token = `${TOKEN_PREFIX}${tokenId++}`;
const href = sanitizeHref(hrefRaw);
if (!href) {
tokenMap.set(token, escapeHtml(label));
return token;
}
tokenMap.set(
token,
`<a class="tach-typography__md-link" href="${escapeHtml(href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)}</a>`
);
return token;
});
let html = escapeHtml(tokenized).replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>").replace(/__([^_\n]+)__/g, "<strong>$1</strong>").replace(/\*([^*\n]+)\*/g, "<em>$1</em>").replace(/_([^_\n]+)_/g, "<em>$1</em>").replace(/~~([^~\n]+)~~/g, "<del>$1</del>").replace(/\n/g, "<br />");
html = html.replace(new RegExp(`${TOKEN_PREFIX}\\d+`, "g"), (token) => tokenMap.get(token) || token);
return html;
};
var createTypographyVariant = (Component, variant) => { var createTypographyVariant = (Component, variant) => {
const Variant = React.forwardRef( const Variant = React.forwardRef(
({ color = "primary", weight = "normal", className, onClick, ...rest }, ref) => /* @__PURE__ */ jsx( ({
color = "primary",
weight = "normal",
className,
onClick,
markdownEnabled = false,
children,
...rest
}, ref) => {
const markdownHtml = markdownEnabled && typeof children === "string" ? tachTypographyMarkdownToHtml(children) : void 0;
const renderedChildren = markdownHtml ? /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: markdownHtml } }) : children;
const contentProps = { children: renderedChildren };
return /* @__PURE__ */ jsx(
Component, Component,
{ {
ref, ref,
@@ -37,9 +90,11 @@ var createTypographyVariant = (Component, variant) => {
clickable: Boolean(onClick) clickable: Boolean(onClick)
}), }),
onClick, onClick,
...rest ...rest,
...contentProps
}
);
} }
)
); );
Variant.displayName = String(variant); Variant.displayName = String(variant);
return Variant; return Variant;

File diff suppressed because one or more lines are too long

View File

@@ -88,6 +88,17 @@
.tach-typography--AppMediumSubtextUnderline, .tach-typography--AppMediumSubtextUnderline,
.ant-typography.tach-typography--AppMediumSubtextUnderline { font-family: Inter, sans-serif; font-size: 11px; font-weight: 400; line-height: 17px; text-decoration: underline; } .ant-typography.tach-typography--AppMediumSubtextUnderline { font-family: Inter, sans-serif; font-size: 11px; font-weight: 400; line-height: 17px; text-decoration: underline; }
.tach-typography .tach-typography__md-link,
.ant-typography.tach-typography .tach-typography__md-link {
color: inherit;
text-decoration: underline;
}
.tach-typography .tach-typography__md-code,
.ant-typography.tach-typography .tach-typography__md-code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
@media (max-width: 575px) { @media (max-width: 575px) {
.tach-typography--AccentLargeTtl, .tach-typography--AccentLargeTtl,
.ant-typography.tach-typography--AccentLargeTtl { .ant-typography.tach-typography--AccentLargeTtl {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@hublib-web/tach-typography", "name": "@hublib-web/tach-typography",
"version": "0.2.0", "version": "0.3.0",
"description": "Cross-framework typography package for React and Angular", "description": "Cross-framework typography package for React and Angular",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",

View File

@@ -1,4 +1,4 @@
import { NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault } from "@angular/common"; import { NgIf, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault } from "@angular/common";
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
@@ -19,6 +19,7 @@ import {
tachTypographyClassList, tachTypographyClassList,
tachTypographyClassName, tachTypographyClassName,
tachTypographyEllipsisStyle, tachTypographyEllipsisStyle,
tachTypographyMarkdownToHtml,
type EllipsisOptions, type EllipsisOptions,
type TypographyClassOptions, type TypographyClassOptions,
type TypographyColor, type TypographyColor,
@@ -291,6 +292,7 @@ export class TachTypographyHostPropsDirective implements OnChanges {
NgSwitch, NgSwitch,
NgSwitchCase, NgSwitchCase,
NgSwitchDefault, NgSwitchDefault,
NgIf,
NgStyle, NgStyle,
], ],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
@@ -310,7 +312,12 @@ export class TachTypographyHostPropsDirective implements OnChanges {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentP">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentP>
<ng-content /> <ng-content />
</ng-template>
</p> </p>
<a <a
*ngSwitchCase="'a'" *ngSwitchCase="'a'"
@@ -326,7 +333,12 @@ export class TachTypographyHostPropsDirective implements OnChanges {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentA">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentA>
<ng-content /> <ng-content />
</ng-template>
</a> </a>
<h1 <h1
*ngSwitchCase="'h1'" *ngSwitchCase="'h1'"
@@ -342,7 +354,12 @@ export class TachTypographyHostPropsDirective implements OnChanges {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH1">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentH1>
<ng-content /> <ng-content />
</ng-template>
</h1> </h1>
<h2 <h2
*ngSwitchCase="'h2'" *ngSwitchCase="'h2'"
@@ -358,7 +375,12 @@ export class TachTypographyHostPropsDirective implements OnChanges {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH2">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentH2>
<ng-content /> <ng-content />
</ng-template>
</h2> </h2>
<h3 <h3
*ngSwitchCase="'h3'" *ngSwitchCase="'h3'"
@@ -374,7 +396,12 @@ export class TachTypographyHostPropsDirective implements OnChanges {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH3">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentH3>
<ng-content /> <ng-content />
</ng-template>
</h3> </h3>
<h4 <h4
*ngSwitchCase="'h4'" *ngSwitchCase="'h4'"
@@ -390,7 +417,12 @@ export class TachTypographyHostPropsDirective implements OnChanges {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentH4">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentH4>
<ng-content /> <ng-content />
</ng-template>
</h4> </h4>
<span <span
*ngSwitchDefault *ngSwitchDefault
@@ -406,17 +438,24 @@ export class TachTypographyHostPropsDirective implements OnChanges {
[ngStyle]="preserveStyle" [ngStyle]="preserveStyle"
(click)="handleClick($event)" (click)="handleClick($event)"
> >
<ng-container *ngIf="markdownEnabled; else tachTypographyContentSpan">
<span [innerHTML]="renderedMarkdown"></span>
</ng-container>
<ng-template #tachTypographyContentSpan>
<ng-content /> <ng-content />
</ng-template>
</span> </span>
</ng-container> </ng-container>
`, `,
}) })
export class TachTypographyComponent { export class TachTypographyComponent implements OnChanges {
@Input("as") hostTag: TachTypographyHostTag = "span"; @Input("as") hostTag: TachTypographyHostTag = "span";
@Input() variant: TypographyVariant = "Body"; @Input() variant: TypographyVariant = "Body";
@Input() color: TypographyColor = "primary"; @Input() color: TypographyColor = "primary";
@Input() weight: TypographyWeight = "normal"; @Input() weight: TypographyWeight = "normal";
@Input() clickable = false; @Input() clickable = false;
@Input() markdownEnabled = false;
@Input() markdown: string | undefined;
@Input() className: string | undefined; @Input() className: string | undefined;
@Input() ellipsis: EllipsisOptions | undefined; @Input() ellipsis: EllipsisOptions | undefined;
@Input() nzProps: TachTypographyNzProps | undefined; @Input() nzProps: TachTypographyNzProps | undefined;
@@ -425,6 +464,14 @@ export class TachTypographyComponent {
@Output() readonly tachClick = new EventEmitter<MouseEvent>(); @Output() readonly tachClick = new EventEmitter<MouseEvent>();
renderedMarkdown = "";
ngOnChanges(_changes: SimpleChanges): void {
this.renderedMarkdown = this.markdownEnabled
? tachTypographyMarkdownToHtml(this.markdown ?? "")
: "";
}
handleClick(event: MouseEvent): void { handleClick(event: MouseEvent): void {
this.tachClick.emit(event); this.tachClick.emit(event);
} }

View File

@@ -4,6 +4,7 @@ import {
tachTypographyClassList, tachTypographyClassList,
tachTypographyClassName, tachTypographyClassName,
tachTypographyEllipsisStyle, tachTypographyEllipsisStyle,
tachTypographyMarkdownToHtml,
} from "./index"; } from "./index";
describe("tachTypographyClassName", () => { describe("tachTypographyClassName", () => {
@@ -54,3 +55,27 @@ describe("tachTypographyEllipsisStyle", () => {
}); });
}); });
}); });
describe("tachTypographyMarkdownToHtml", () => {
it("parses inline markdown tags", () => {
expect(
tachTypographyMarkdownToHtml("**bold** *italic* ~~strike~~ `code`"),
).toBe(
"<strong>bold</strong> <em>italic</em> <del>strike</del> <code class=\"tach-typography__md-code\">code</code>",
);
});
it("sanitizes html", () => {
expect(tachTypographyMarkdownToHtml("<script>alert(1)</script>")).toBe(
"&lt;script&gt;alert(1)&lt;/script&gt;",
);
});
it("keeps only safe link protocols", () => {
expect(
tachTypographyMarkdownToHtml("[safe](https://example.com) [unsafe](javascript:alert-1)"),
).toBe(
"<a class=\"tach-typography__md-link\" href=\"https://example.com\" target=\"_blank\" rel=\"noopener noreferrer\">safe</a> unsafe",
);
});
});

View File

@@ -1,3 +1,4 @@
export * from "./types"; export * from "./types";
export * from "./classnames"; export * from "./classnames";
export * from "./ellipsis"; export * from "./ellipsis";
export * from "./markdown";

View File

@@ -0,0 +1,65 @@
const TOKEN_PREFIX = "TACHMDTOKEN";
const SAFE_HREF_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
const escapeHtml = (value: string): string =>
value
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
const normalizeMarkdown = (value: string): string => value.replace(/\r\n?/g, "\n");
const sanitizeHref = (value: string): string | null => {
const href = value.trim();
if (!href || !SAFE_HREF_PATTERN.test(href)) {
return null;
}
return href;
};
export const tachTypographyMarkdownToHtml = (markdown: string): string => {
const source = normalizeMarkdown(markdown);
const tokenMap = new Map<string, string>();
let tokenId = 0;
const tokenized = source
.replace(/`([^`\n]+)`/g, (_match, code: string) => {
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: string, hrefRaw: string) => {
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;
};

View File

@@ -7,6 +7,7 @@ import type { TextProps } from "antd/lib/typography/Text";
import type { TitleProps } from "antd/lib/typography/Title"; import type { TitleProps } from "antd/lib/typography/Title";
import { import {
tachTypographyMarkdownToHtml,
tachTypographyClassName, tachTypographyClassName,
type TypographyColor, type TypographyColor,
type TypographyVariant, type TypographyVariant,
@@ -18,6 +19,8 @@ interface AdditionalProps {
weight?: TypographyWeight; weight?: TypographyWeight;
onClick?: (event: React.MouseEvent<HTMLElement>) => void; onClick?: (event: React.MouseEvent<HTMLElement>) => void;
className?: string | undefined; className?: string | undefined;
markdownEnabled?: boolean;
children?: React.ReactNode;
} }
const createTypographyVariant = <P extends object>( const createTypographyVariant = <P extends object>(
@@ -25,7 +28,30 @@ const createTypographyVariant = <P extends object>(
variant: TypographyVariant, variant: TypographyVariant,
) => { ) => {
const Variant = React.forwardRef<HTMLElement, P & AdditionalProps>( const Variant = React.forwardRef<HTMLElement, P & AdditionalProps>(
({ color = "primary", weight = "normal", className, onClick, ...rest }, ref) => ( (
{
color = "primary",
weight = "normal",
className,
onClick,
markdownEnabled = false,
children,
...rest
},
ref,
) => {
const markdownHtml =
markdownEnabled && typeof children === "string"
? tachTypographyMarkdownToHtml(children)
: undefined;
const renderedChildren = markdownHtml ? (
<span dangerouslySetInnerHTML={{ __html: markdownHtml }} />
) : (
children
);
const contentProps = { children: renderedChildren } as unknown as P;
return (
<Component <Component
ref={ref as never} ref={ref as never}
className={tachTypographyClassName({ className={tachTypographyClassName({
@@ -37,8 +63,10 @@ const createTypographyVariant = <P extends object>(
})} })}
onClick={onClick} onClick={onClick}
{...(rest as P)} {...(rest as P)}
{...contentProps}
/> />
), );
},
); );
Variant.displayName = String(variant); Variant.displayName = String(variant);

View File

@@ -0,0 +1,157 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
import {
TYPOGRAPHY_COLORS,
TYPOGRAPHY_VARIANTS,
type TypographyColor,
type TypographyVariant,
type TypographyWeight,
} from "../core";
import { TachTypography } from "../react";
type TypographyNamespace = keyof typeof TachTypography;
type VariantComponentProps = {
children?: React.ReactNode;
className?: string;
color?: TypographyColor;
ellipsis?: boolean | { rows?: number };
href?: string;
level?: 1 | 2 | 3 | 4 | 5;
markdownEnabled?: boolean;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
weight?: TypographyWeight;
};
type VariantComponent = React.ComponentType<VariantComponentProps>;
const getVariantComponent = (
namespace: TypographyNamespace,
variant: TypographyVariant,
): VariantComponent => {
return (TachTypography[namespace] as unknown as Record<TypographyVariant, VariantComponent>)[variant];
};
interface MarkdownArgs {
namespace: TypographyNamespace;
variant: TypographyVariant;
color: TypographyColor;
weight: TypographyWeight;
markdownEnabled: boolean;
content: string;
href: string;
titleLevel: 1 | 2 | 3 | 4 | 5;
}
const renderMarkdownTypography = (args: MarkdownArgs) => {
const Component = getVariantComponent(args.namespace, args.variant);
const componentProps: VariantComponentProps = {
color: args.color,
weight: args.weight,
markdownEnabled: args.markdownEnabled,
};
if (args.namespace === "Link") {
componentProps.href = args.href;
}
if (args.namespace === "Title") {
componentProps.level = args.titleLevel;
}
return (
<div className="tach-story-surface tach-story-stack">
<Component {...componentProps}>{args.content}</Component>
</div>
);
};
const meta: Meta<MarkdownArgs> = {
title: "TachTypography/Markdown",
tags: ["autodocs"],
render: renderMarkdownTypography,
args: {
namespace: "Text",
variant: "Body",
color: "primary",
weight: "normal",
markdownEnabled: true,
content: "**Bold** _italic_ ~~strike~~ `code` [Docs](https://example.com)",
href: "https://example.com",
titleLevel: 3,
},
argTypes: {
namespace: {
control: "select",
options: ["Text", "Paragraph", "Link", "Title"],
},
variant: {
control: "select",
options: TYPOGRAPHY_VARIANTS,
},
color: {
control: "select",
options: TYPOGRAPHY_COLORS,
},
weight: {
control: "inline-radio",
options: ["normal", "bold"],
},
markdownEnabled: {
control: "boolean",
},
content: {
control: "text",
},
href: {
control: "text",
description: "Works for Link namespace",
},
titleLevel: {
control: "inline-radio",
options: [1, 2, 3, 4, 5],
description: "Works for Title namespace",
},
},
parameters: {
docs: {
description: {
component:
"Markdown rendering in existing TachTypography components via the markdownEnabled prop.",
},
},
},
};
export default meta;
type Story = StoryObj<typeof meta>;
export const ReactMarkdown: Story = {};
export const MarkdownDisabled: Story = {
args: {
markdownEnabled: false,
content: "**Bold** _italic_ markdown is shown as plain text",
},
};
export const AngularUsage: Story = {
render: () => (
<div className="tach-story-surface tach-story-stack">
<h4>Angular usage</h4>
<pre>
<code>
{`<tach-typography
variant="Body"
[markdownEnabled]="true"
[markdown]="'**Bold** _italic_ [Docs](https://example.com)'"
></tach-typography>`}
</code>
</pre>
</div>
),
};

View File

@@ -67,6 +67,20 @@ export const PropsMatrix: Story = {
<td>tachTypographyClassName</td> <td>tachTypographyClassName</td>
<td>Merges with token classes</td> <td>Merges with token classes</td>
</tr> </tr>
<tr>
<td>markdownEnabled</td>
<td>boolean</td>
<td>markdownEnabled</td>
<td>[markdownEnabled]</td>
<td>Enables markdown rendering from content string</td>
</tr>
<tr>
<td>markdown</td>
<td>string</td>
<td>children string</td>
<td>[markdown]</td>
<td>Angular markdown source string</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@@ -88,6 +88,17 @@
.tach-typography--AppMediumSubtextUnderline, .tach-typography--AppMediumSubtextUnderline,
.ant-typography.tach-typography--AppMediumSubtextUnderline { font-family: Inter, sans-serif; font-size: 11px; font-weight: 400; line-height: 17px; text-decoration: underline; } .ant-typography.tach-typography--AppMediumSubtextUnderline { font-family: Inter, sans-serif; font-size: 11px; font-weight: 400; line-height: 17px; text-decoration: underline; }
.tach-typography .tach-typography__md-link,
.ant-typography.tach-typography .tach-typography__md-link {
color: inherit;
text-decoration: underline;
}
.tach-typography .tach-typography__md-code,
.ant-typography.tach-typography .tach-typography__md-code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
@media (max-width: 575px) { @media (max-width: 575px) {
.tach-typography--AccentLargeTtl, .tach-typography--AccentLargeTtl,
.ant-typography.tach-typography--AccentLargeTtl { .ant-typography.tach-typography--AccentLargeTtl {