chore: Монорепо с общими пакетами
This commit is contained in:
18
packages/content-suggestions/.storybook/main.ts
Normal file
18
packages/content-suggestions/.storybook/main.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { StorybookConfig } from "@storybook/react-vite";
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ["../src/**/*.stories.@(ts|tsx)"],
|
||||
addons: ["@storybook/addon-essentials"],
|
||||
framework: {
|
||||
name: "@storybook/react-vite",
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: "tag",
|
||||
},
|
||||
core: {
|
||||
disableTelemetry: true,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
19
packages/content-suggestions/.storybook/preview.ts
Normal file
19
packages/content-suggestions/.storybook/preview.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { Preview } from "@storybook/react";
|
||||
|
||||
import "antd/dist/reset.css";
|
||||
import "@hublib-web/tach-typography/styles.css";
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
layout: "padded",
|
||||
controls: {
|
||||
expanded: true,
|
||||
sort: "requiredFirst",
|
||||
},
|
||||
actions: {
|
||||
argTypesRegex: "^on[A-Z].*",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
||||
98
packages/content-suggestions/README.md
Normal file
98
packages/content-suggestions/README.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# @hublib-web/content-suggestions
|
||||
|
||||
Cross-framework content text/title renderer with support for mentions, tags and links.
|
||||
|
||||
## Features
|
||||
|
||||
- Shared parser in `core` (`findAllEntities`, `findMentions`, `findTags`, `findLinks`).
|
||||
- React UI components in `react`:
|
||||
- `ContentText`
|
||||
- `ContentTextWithSuggestions`
|
||||
- `ContentTitleWithSuggestions`
|
||||
- Angular adapter in `angular` for rendering/tokenization integration.
|
||||
- Depends on `@hublib-web/tach-typography` for visual consistency.
|
||||
- Business logic (API requests for mentions/tags) stays in consumer application.
|
||||
|
||||
## Install from Git (SSH tag)
|
||||
|
||||
```bash
|
||||
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.
|
||||
|
||||
## Install inside this monorepo
|
||||
|
||||
```bash
|
||||
yarn add @hublib-web/content-suggestions
|
||||
```
|
||||
|
||||
## Release this package
|
||||
|
||||
1. Bump `version` in `packages/content-suggestions/package.json`.
|
||||
2. Build package artifacts:
|
||||
|
||||
```bash
|
||||
yarn workspace @hublib-web/content-suggestions build
|
||||
```
|
||||
|
||||
3. Commit release files:
|
||||
|
||||
```bash
|
||||
git add packages/content-suggestions/package.json packages/content-suggestions/dist
|
||||
git commit -m "release(content-suggestions): v0.1.0"
|
||||
```
|
||||
|
||||
4. Create and push tag:
|
||||
|
||||
```bash
|
||||
git tag -a content-suggestions-v0.1.0 -m "@hublib-web/content-suggestions v0.1.0"
|
||||
git push origin main --follow-tags
|
||||
```
|
||||
|
||||
Detailed docs:
|
||||
|
||||
- [Release policy](../../docs/release-policy.md)
|
||||
- [Git installation](../../docs/git-installation.md)
|
||||
|
||||
## React usage
|
||||
|
||||
```tsx
|
||||
import { ContentTextWithSuggestions } from "@hublib-web/content-suggestions/react";
|
||||
|
||||
<ContentTextWithSuggestions text={text} />;
|
||||
```
|
||||
|
||||
With app-specific mention business logic:
|
||||
|
||||
```tsx
|
||||
<ContentTextWithSuggestions
|
||||
text={text}
|
||||
renderMention={(entity) => <MyMention entity={entity} />}
|
||||
renderTag={(entity) => <MyTagLink entity={entity} />}
|
||||
/>;
|
||||
```
|
||||
|
||||
By default, tags are rendered as plain styled text (not links).
|
||||
|
||||
## Angular usage
|
||||
|
||||
```ts
|
||||
import { createAngularContentTokens } from "@hublib-web/content-suggestions/angular";
|
||||
|
||||
const tokens = createAngularContentTokens(text);
|
||||
```
|
||||
|
||||
## Storybook (dev/design system)
|
||||
|
||||
Run from repository root:
|
||||
|
||||
```bash
|
||||
yarn workspace @hublib-web/content-suggestions storybook
|
||||
```
|
||||
|
||||
Build static Storybook:
|
||||
|
||||
```bash
|
||||
yarn workspace @hublib-web/content-suggestions storybook:build
|
||||
```
|
||||
141
packages/content-suggestions/dist/angular/index.cjs
vendored
Normal file
141
packages/content-suggestions/dist/angular/index.cjs
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
'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
|
||||
1
packages/content-suggestions/dist/angular/index.cjs.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/index.cjs.map
vendored
Normal file
File diff suppressed because one or more lines are too long
33
packages/content-suggestions/dist/angular/index.d.cts
vendored
Normal file
33
packages/content-suggestions/dist/angular/index.d.cts
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
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 };
|
||||
33
packages/content-suggestions/dist/angular/index.d.ts
vendored
Normal file
33
packages/content-suggestions/dist/angular/index.d.ts
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
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 };
|
||||
137
packages/content-suggestions/dist/angular/index.js
vendored
Normal file
137
packages/content-suggestions/dist/angular/index.js
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
// 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
|
||||
1
packages/content-suggestions/dist/angular/index.js.map
vendored
Normal file
1
packages/content-suggestions/dist/angular/index.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
97
packages/content-suggestions/dist/core/index.cjs
vendored
Normal file
97
packages/content-suggestions/dist/core/index.cjs
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
'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);
|
||||
};
|
||||
var 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
|
||||
};
|
||||
};
|
||||
|
||||
exports.findAllEntities = findAllEntities;
|
||||
exports.findLinks = findLinks;
|
||||
exports.findMentions = findMentions;
|
||||
exports.findTags = findTags;
|
||||
exports.mentionLinkRegexp = mentionLinkRegexp;
|
||||
exports.parseMention = parseMention;
|
||||
exports.processContent = processContent;
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
1
packages/content-suggestions/dist/core/index.cjs.map
vendored
Normal file
1
packages/content-suggestions/dist/core/index.cjs.map
vendored
Normal file
File diff suppressed because one or more lines are too long
12
packages/content-suggestions/dist/core/index.d.cts
vendored
Normal file
12
packages/content-suggestions/dist/core/index.d.cts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
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';
|
||||
|
||||
declare const mentionLinkRegexp: RegExp;
|
||||
declare const parseMention: (mention: string) => ParsedMention | null;
|
||||
declare const findMentions: (text: string) => MentionEntity[];
|
||||
declare const findTags: (content: string) => TagEntity[];
|
||||
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 };
|
||||
12
packages/content-suggestions/dist/core/index.d.ts
vendored
Normal file
12
packages/content-suggestions/dist/core/index.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
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';
|
||||
|
||||
declare const mentionLinkRegexp: RegExp;
|
||||
declare const parseMention: (mention: string) => ParsedMention | null;
|
||||
declare const findMentions: (text: string) => MentionEntity[];
|
||||
declare const findTags: (content: string) => TagEntity[];
|
||||
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 };
|
||||
89
packages/content-suggestions/dist/core/index.js
vendored
Normal file
89
packages/content-suggestions/dist/core/index.js
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
// 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);
|
||||
};
|
||||
var 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
|
||||
};
|
||||
};
|
||||
|
||||
export { findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent };
|
||||
//# sourceMappingURL=index.js.map
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/content-suggestions/dist/core/index.js.map
vendored
Normal file
1
packages/content-suggestions/dist/core/index.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
396
packages/content-suggestions/dist/react/index.cjs
vendored
Normal file
396
packages/content-suggestions/dist/react/index.cjs
vendored
Normal file
@@ -0,0 +1,396 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var react = require('@hublib-web/tach-typography/react');
|
||||
var jsxRuntime = require('react/jsx-runtime');
|
||||
|
||||
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
||||
|
||||
var React__default = /*#__PURE__*/_interopDefault(React);
|
||||
|
||||
// src/react/components/content-text.tsx
|
||||
|
||||
// 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);
|
||||
};
|
||||
var 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
|
||||
};
|
||||
};
|
||||
var joinClassName = (...values) => values.filter(Boolean).join(" ");
|
||||
var baseSelectableStyle = {
|
||||
whiteSpace: "pre-wrap",
|
||||
WebkitTouchCallout: "default",
|
||||
WebkitUserSelect: "text",
|
||||
KhtmlUserSelect: "text",
|
||||
MozUserSelect: "text",
|
||||
msUserSelect: "text",
|
||||
userSelect: "text"
|
||||
};
|
||||
var blurStyle = {
|
||||
filter: "blur(3px)",
|
||||
WebkitUserSelect: "none",
|
||||
KhtmlUserSelect: "none",
|
||||
MozUserSelect: "none",
|
||||
msUserSelect: "none",
|
||||
userSelect: "none",
|
||||
pointerEvents: "none"
|
||||
};
|
||||
var ReadMoreButton = ({ onClick, symbol = "\u0427\u0438\u0442\u0430\u0442\u044C \u043F\u043E\u043B\u043D\u043E\u0441\u0442\u044C\u044E", expanded = false, TitleComponent }) => /* @__PURE__ */ jsxRuntime.jsx(
|
||||
TitleComponent,
|
||||
{
|
||||
onClick: (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onClick(event);
|
||||
},
|
||||
weight: "bold",
|
||||
level: 5,
|
||||
children: typeof symbol === "function" ? symbol(expanded) : symbol
|
||||
}
|
||||
);
|
||||
var ContentText = React__default.default.memo(
|
||||
({
|
||||
text,
|
||||
className,
|
||||
ellipsis = false,
|
||||
blur = false,
|
||||
weight = "normal",
|
||||
style,
|
||||
onView,
|
||||
renderMention,
|
||||
renderTag,
|
||||
renderLink,
|
||||
ParagraphComponent = react.TachTypography.Paragraph.Body,
|
||||
TextComponent = react.TachTypography.Text.Body,
|
||||
TitleComponent = react.TachTypography.Title.Body,
|
||||
...props
|
||||
}) => {
|
||||
const containerRef = React.useRef(null);
|
||||
const [expanded, setExpanded] = React.useState(false);
|
||||
const [containerInsideView, setContainerInsideView] = React.useState(false);
|
||||
const [viewed, setViewed] = React.useState(false);
|
||||
const content = text || "";
|
||||
const entities = React.useMemo(() => findAllEntities(content), [content]);
|
||||
const wrapWithKey = (node, key) => {
|
||||
if (React__default.default.isValidElement(node)) {
|
||||
return React__default.default.cloneElement(node, { key });
|
||||
}
|
||||
return /* @__PURE__ */ jsxRuntime.jsx(React__default.default.Fragment, { children: node }, key);
|
||||
};
|
||||
const buildMentionNode = (entity, index) => {
|
||||
const defaultNode = /* @__PURE__ */ jsxRuntime.jsx(TextComponent, { color: "link", weight, children: entity.displayText });
|
||||
const customNode = renderMention?.(entity, index) ?? defaultNode;
|
||||
return wrapWithKey(customNode, `mention-${entity.start}-${index}`);
|
||||
};
|
||||
const buildTagNode = (entity, index) => {
|
||||
const defaultNode = /* @__PURE__ */ jsxRuntime.jsx(TextComponent, { color: "link", weight, children: entity.text });
|
||||
const customNode = renderTag?.(entity, index) ?? defaultNode;
|
||||
return wrapWithKey(customNode, `tag-${entity.start}-${index}`);
|
||||
};
|
||||
const buildLinkNode = (entity, index) => {
|
||||
const defaultNode = /* @__PURE__ */ jsxRuntime.jsx(
|
||||
react.TachTypography.Link.Body,
|
||||
{
|
||||
target: "_blank",
|
||||
referrerPolicy: "no-referrer",
|
||||
color: "link",
|
||||
weight,
|
||||
href: entity.url,
|
||||
children: entity.text
|
||||
}
|
||||
);
|
||||
const customNode = renderLink?.(entity, index) ?? defaultNode;
|
||||
return wrapWithKey(customNode, `link-${entity.start}-${index}`);
|
||||
};
|
||||
const buildParts = (upto = null) => {
|
||||
let lastIndex = 0;
|
||||
const nodes = [];
|
||||
for (const [i, entity] of entities.entries()) {
|
||||
if (upto !== null && entity.start >= upto) {
|
||||
break;
|
||||
}
|
||||
const textEnd = upto !== null ? Math.min(entity.start, upto) : entity.start;
|
||||
if (entity.start > lastIndex && lastIndex < textEnd) {
|
||||
nodes.push(content.slice(lastIndex, textEnd));
|
||||
}
|
||||
if (upto === null || entity.end <= upto) {
|
||||
if (entity.type === "mention") {
|
||||
nodes.push(buildMentionNode(entity, i));
|
||||
} else if (entity.type === "tag") {
|
||||
nodes.push(buildTagNode(entity, i));
|
||||
} else if (entity.type === "link") {
|
||||
nodes.push(buildLinkNode(entity, i));
|
||||
}
|
||||
}
|
||||
lastIndex = entity.end;
|
||||
}
|
||||
if (upto === null) {
|
||||
if (lastIndex < content.length) {
|
||||
nodes.push(content.slice(lastIndex));
|
||||
}
|
||||
} else if (lastIndex < upto) {
|
||||
nodes.push(content.slice(lastIndex, upto));
|
||||
}
|
||||
return nodes;
|
||||
};
|
||||
const ellipsisConfig = ellipsis && typeof ellipsis === "object" ? ellipsis : null;
|
||||
const expandedFromProps = ellipsisConfig && typeof ellipsisConfig.expanded === "boolean" ? ellipsisConfig.expanded : void 0;
|
||||
const isExpandedControlled = expandedFromProps !== void 0;
|
||||
const mergedExpanded = isExpandedControlled ? expandedFromProps : expanded;
|
||||
const handleExpand = React.useCallback(
|
||||
(event) => {
|
||||
if (!isExpandedControlled) {
|
||||
setExpanded(true);
|
||||
}
|
||||
ellipsisConfig?.onExpand?.(event, { expanded: true });
|
||||
},
|
||||
[ellipsisConfig, isExpandedControlled]
|
||||
);
|
||||
React.useEffect(() => {
|
||||
if (isExpandedControlled) {
|
||||
return;
|
||||
}
|
||||
if (ellipsisConfig && "count" in ellipsisConfig) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
if (content.length <= count && !expanded) {
|
||||
setExpanded(true);
|
||||
}
|
||||
}
|
||||
}, [content.length, ellipsisConfig, expanded, isExpandedControlled]);
|
||||
React.useEffect(() => {
|
||||
if (mergedExpanded && !viewed && containerInsideView) {
|
||||
setViewed(true);
|
||||
onView?.();
|
||||
}
|
||||
}, [mergedExpanded, viewed, containerInsideView, onView]);
|
||||
React.useEffect(() => {
|
||||
const ref = containerRef.current;
|
||||
if (!ref) {
|
||||
return;
|
||||
}
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
const entry = entries[0];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
if (entry.isIntersecting && !containerInsideView) {
|
||||
setContainerInsideView(true);
|
||||
}
|
||||
}, { threshold: 0.5 });
|
||||
observer.observe(ref);
|
||||
return () => {
|
||||
observer.unobserve(ref);
|
||||
};
|
||||
}, [containerInsideView]);
|
||||
const mergedStyle = {
|
||||
...baseSelectableStyle,
|
||||
...blur ? blurStyle : void 0,
|
||||
...style
|
||||
};
|
||||
if (ellipsisConfig && "count" in ellipsisConfig) {
|
||||
const { count, expandable } = ellipsisConfig;
|
||||
if (!mergedExpanded && count && content.length > count) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
const truncatedNodes = buildParts(cutoff);
|
||||
return /* @__PURE__ */ jsxRuntime.jsxs(
|
||||
ParagraphComponent,
|
||||
{
|
||||
ref: containerRef,
|
||||
weight,
|
||||
className: joinClassName(className),
|
||||
style: mergedStyle,
|
||||
...props,
|
||||
children: [
|
||||
truncatedNodes,
|
||||
"\u2026",
|
||||
expandable && /* @__PURE__ */ jsxRuntime.jsx(
|
||||
ReadMoreButton,
|
||||
{
|
||||
symbol: ellipsisConfig.symbol,
|
||||
onClick: handleExpand,
|
||||
expanded: mergedExpanded,
|
||||
TitleComponent
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
if (ellipsisConfig && "rows" in ellipsisConfig) {
|
||||
const paragraphEllipsis = mergedExpanded ? false : {
|
||||
...ellipsisConfig,
|
||||
symbol: ellipsisConfig.expandable ? /* @__PURE__ */ jsxRuntime.jsx(
|
||||
ReadMoreButton,
|
||||
{
|
||||
symbol: ellipsisConfig.symbol,
|
||||
onClick: handleExpand,
|
||||
expanded: mergedExpanded,
|
||||
TitleComponent
|
||||
}
|
||||
) : ellipsisConfig.symbol
|
||||
};
|
||||
return /* @__PURE__ */ jsxRuntime.jsx(
|
||||
ParagraphComponent,
|
||||
{
|
||||
ref: containerRef,
|
||||
weight,
|
||||
className: joinClassName(className),
|
||||
style: mergedStyle,
|
||||
ellipsis: paragraphEllipsis,
|
||||
...props,
|
||||
children: buildParts()
|
||||
}
|
||||
);
|
||||
}
|
||||
return /* @__PURE__ */ jsxRuntime.jsx(
|
||||
ParagraphComponent,
|
||||
{
|
||||
ref: containerRef,
|
||||
weight,
|
||||
className: joinClassName(className),
|
||||
style: mergedStyle,
|
||||
...props,
|
||||
children: buildParts()
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
ContentText.displayName = "ContentText";
|
||||
var DefaultMention = ({ entity }) => /* @__PURE__ */ jsxRuntime.jsx(react.TachTypography.Text.Body, { color: "link", children: entity.displayText });
|
||||
var DefaultTag = ({ entity }) => /* @__PURE__ */ jsxRuntime.jsx(react.TachTypography.Text.Body, { color: "link", children: entity.text });
|
||||
var ContentTextWithSuggestions = ({
|
||||
renderMention,
|
||||
renderTag,
|
||||
...props
|
||||
}) => {
|
||||
return /* @__PURE__ */ jsxRuntime.jsx(
|
||||
ContentText,
|
||||
{
|
||||
...props,
|
||||
renderMention: (entity, index) => renderMention ? renderMention(entity, index) : /* @__PURE__ */ jsxRuntime.jsx(DefaultMention, { entity }),
|
||||
renderTag: (entity, index) => renderTag ? renderTag(entity, index) : /* @__PURE__ */ jsxRuntime.jsx(DefaultTag, { entity })
|
||||
}
|
||||
);
|
||||
};
|
||||
var ContentTitleWithSuggestions = ({
|
||||
text,
|
||||
ellipsis,
|
||||
blur = false,
|
||||
...rest
|
||||
}) => {
|
||||
const normalizedEllipsis = ellipsis === void 0 ? { rows: 2 } : ellipsis;
|
||||
const textProps = text === void 0 ? {} : { text };
|
||||
return /* @__PURE__ */ jsxRuntime.jsx(
|
||||
ContentTextWithSuggestions,
|
||||
{
|
||||
weight: "bold",
|
||||
blur,
|
||||
ellipsis: normalizedEllipsis,
|
||||
...textProps,
|
||||
...rest
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
exports.ContentText = ContentText;
|
||||
exports.ContentTextWithSuggestions = ContentTextWithSuggestions;
|
||||
exports.ContentTitleWithSuggestions = ContentTitleWithSuggestions;
|
||||
exports.findAllEntities = findAllEntities;
|
||||
exports.findLinks = findLinks;
|
||||
exports.findMentions = findMentions;
|
||||
exports.findTags = findTags;
|
||||
exports.mentionLinkRegexp = mentionLinkRegexp;
|
||||
exports.parseMention = parseMention;
|
||||
exports.processContent = processContent;
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
1
packages/content-suggestions/dist/react/index.cjs.map
vendored
Normal file
1
packages/content-suggestions/dist/react/index.cjs.map
vendored
Normal file
File diff suppressed because one or more lines are too long
48
packages/content-suggestions/dist/react/index.d.cts
vendored
Normal file
48
packages/content-suggestions/dist/react/index.d.cts
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
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 { 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;
|
||||
rows?: never;
|
||||
expandable?: boolean;
|
||||
} & Partial<EllipsisConfig>) | ({
|
||||
rows: number;
|
||||
count?: never;
|
||||
expandable?: boolean;
|
||||
} & Partial<EllipsisConfig>) | false;
|
||||
type ParagraphBodyProps = ComponentProps<typeof TachTypography.Paragraph.Body>;
|
||||
type TextBodyProps = ComponentProps<typeof TachTypography.Text.Body>;
|
||||
type TitleBodyProps = ComponentProps<typeof TachTypography.Title.Body>;
|
||||
interface ContentTextProps {
|
||||
className?: string;
|
||||
weight?: TextBodyProps["weight"];
|
||||
text?: string | null;
|
||||
ellipsis?: CustomEllipsisConfig;
|
||||
blur?: boolean;
|
||||
style?: TextBodyProps["style"];
|
||||
onView?: () => void;
|
||||
renderMention?: (entity: MentionEntity, index: number) => ReactNode;
|
||||
renderTag?: (entity: TagEntity, index: number) => ReactNode;
|
||||
renderLink?: (entity: LinkEntity, index: number) => ReactNode;
|
||||
ParagraphComponent?: React.ComponentType<ParagraphBodyProps>;
|
||||
TextComponent?: React.ComponentType<TextBodyProps>;
|
||||
TitleComponent?: React.ComponentType<TitleBodyProps>;
|
||||
}
|
||||
declare const ContentText: React.NamedExoticComponent<ContentTextProps>;
|
||||
|
||||
type BaseContentTextProps = Omit<ComponentProps<typeof ContentText>, "renderMention" | "renderTag"> & {
|
||||
renderMention?: (entity: MentionEntity, index: number) => React.ReactNode;
|
||||
renderTag?: (entity: TagEntity, index: number) => React.ReactNode;
|
||||
};
|
||||
declare const ContentTextWithSuggestions: ({ renderMention, renderTag, ...props }: BaseContentTextProps) => react_jsx_runtime.JSX.Element;
|
||||
|
||||
interface ContentTitleWithSuggestionsProps extends 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 };
|
||||
48
packages/content-suggestions/dist/react/index.d.ts
vendored
Normal file
48
packages/content-suggestions/dist/react/index.d.ts
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
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 { 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;
|
||||
rows?: never;
|
||||
expandable?: boolean;
|
||||
} & Partial<EllipsisConfig>) | ({
|
||||
rows: number;
|
||||
count?: never;
|
||||
expandable?: boolean;
|
||||
} & Partial<EllipsisConfig>) | false;
|
||||
type ParagraphBodyProps = ComponentProps<typeof TachTypography.Paragraph.Body>;
|
||||
type TextBodyProps = ComponentProps<typeof TachTypography.Text.Body>;
|
||||
type TitleBodyProps = ComponentProps<typeof TachTypography.Title.Body>;
|
||||
interface ContentTextProps {
|
||||
className?: string;
|
||||
weight?: TextBodyProps["weight"];
|
||||
text?: string | null;
|
||||
ellipsis?: CustomEllipsisConfig;
|
||||
blur?: boolean;
|
||||
style?: TextBodyProps["style"];
|
||||
onView?: () => void;
|
||||
renderMention?: (entity: MentionEntity, index: number) => ReactNode;
|
||||
renderTag?: (entity: TagEntity, index: number) => ReactNode;
|
||||
renderLink?: (entity: LinkEntity, index: number) => ReactNode;
|
||||
ParagraphComponent?: React.ComponentType<ParagraphBodyProps>;
|
||||
TextComponent?: React.ComponentType<TextBodyProps>;
|
||||
TitleComponent?: React.ComponentType<TitleBodyProps>;
|
||||
}
|
||||
declare const ContentText: React.NamedExoticComponent<ContentTextProps>;
|
||||
|
||||
type BaseContentTextProps = Omit<ComponentProps<typeof ContentText>, "renderMention" | "renderTag"> & {
|
||||
renderMention?: (entity: MentionEntity, index: number) => React.ReactNode;
|
||||
renderTag?: (entity: TagEntity, index: number) => React.ReactNode;
|
||||
};
|
||||
declare const ContentTextWithSuggestions: ({ renderMention, renderTag, ...props }: BaseContentTextProps) => react_jsx_runtime.JSX.Element;
|
||||
|
||||
interface ContentTitleWithSuggestionsProps extends 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 };
|
||||
381
packages/content-suggestions/dist/react/index.js
vendored
Normal file
381
packages/content-suggestions/dist/react/index.js
vendored
Normal file
@@ -0,0 +1,381 @@
|
||||
import React, { useRef, useState, useMemo, useCallback, useEffect } from 'react';
|
||||
import { TachTypography } from '@hublib-web/tach-typography/react';
|
||||
import { jsxs, jsx } from 'react/jsx-runtime';
|
||||
|
||||
// src/react/components/content-text.tsx
|
||||
|
||||
// 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);
|
||||
};
|
||||
var 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
|
||||
};
|
||||
};
|
||||
var joinClassName = (...values) => values.filter(Boolean).join(" ");
|
||||
var baseSelectableStyle = {
|
||||
whiteSpace: "pre-wrap",
|
||||
WebkitTouchCallout: "default",
|
||||
WebkitUserSelect: "text",
|
||||
KhtmlUserSelect: "text",
|
||||
MozUserSelect: "text",
|
||||
msUserSelect: "text",
|
||||
userSelect: "text"
|
||||
};
|
||||
var blurStyle = {
|
||||
filter: "blur(3px)",
|
||||
WebkitUserSelect: "none",
|
||||
KhtmlUserSelect: "none",
|
||||
MozUserSelect: "none",
|
||||
msUserSelect: "none",
|
||||
userSelect: "none",
|
||||
pointerEvents: "none"
|
||||
};
|
||||
var ReadMoreButton = ({ onClick, symbol = "\u0427\u0438\u0442\u0430\u0442\u044C \u043F\u043E\u043B\u043D\u043E\u0441\u0442\u044C\u044E", expanded = false, TitleComponent }) => /* @__PURE__ */ jsx(
|
||||
TitleComponent,
|
||||
{
|
||||
onClick: (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onClick(event);
|
||||
},
|
||||
weight: "bold",
|
||||
level: 5,
|
||||
children: typeof symbol === "function" ? symbol(expanded) : symbol
|
||||
}
|
||||
);
|
||||
var ContentText = React.memo(
|
||||
({
|
||||
text,
|
||||
className,
|
||||
ellipsis = false,
|
||||
blur = false,
|
||||
weight = "normal",
|
||||
style,
|
||||
onView,
|
||||
renderMention,
|
||||
renderTag,
|
||||
renderLink,
|
||||
ParagraphComponent = TachTypography.Paragraph.Body,
|
||||
TextComponent = TachTypography.Text.Body,
|
||||
TitleComponent = TachTypography.Title.Body,
|
||||
...props
|
||||
}) => {
|
||||
const containerRef = useRef(null);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [containerInsideView, setContainerInsideView] = useState(false);
|
||||
const [viewed, setViewed] = useState(false);
|
||||
const content = text || "";
|
||||
const entities = useMemo(() => findAllEntities(content), [content]);
|
||||
const wrapWithKey = (node, key) => {
|
||||
if (React.isValidElement(node)) {
|
||||
return React.cloneElement(node, { key });
|
||||
}
|
||||
return /* @__PURE__ */ jsx(React.Fragment, { children: node }, key);
|
||||
};
|
||||
const buildMentionNode = (entity, index) => {
|
||||
const defaultNode = /* @__PURE__ */ jsx(TextComponent, { color: "link", weight, children: entity.displayText });
|
||||
const customNode = renderMention?.(entity, index) ?? defaultNode;
|
||||
return wrapWithKey(customNode, `mention-${entity.start}-${index}`);
|
||||
};
|
||||
const buildTagNode = (entity, index) => {
|
||||
const defaultNode = /* @__PURE__ */ jsx(TextComponent, { color: "link", weight, children: entity.text });
|
||||
const customNode = renderTag?.(entity, index) ?? defaultNode;
|
||||
return wrapWithKey(customNode, `tag-${entity.start}-${index}`);
|
||||
};
|
||||
const buildLinkNode = (entity, index) => {
|
||||
const defaultNode = /* @__PURE__ */ jsx(
|
||||
TachTypography.Link.Body,
|
||||
{
|
||||
target: "_blank",
|
||||
referrerPolicy: "no-referrer",
|
||||
color: "link",
|
||||
weight,
|
||||
href: entity.url,
|
||||
children: entity.text
|
||||
}
|
||||
);
|
||||
const customNode = renderLink?.(entity, index) ?? defaultNode;
|
||||
return wrapWithKey(customNode, `link-${entity.start}-${index}`);
|
||||
};
|
||||
const buildParts = (upto = null) => {
|
||||
let lastIndex = 0;
|
||||
const nodes = [];
|
||||
for (const [i, entity] of entities.entries()) {
|
||||
if (upto !== null && entity.start >= upto) {
|
||||
break;
|
||||
}
|
||||
const textEnd = upto !== null ? Math.min(entity.start, upto) : entity.start;
|
||||
if (entity.start > lastIndex && lastIndex < textEnd) {
|
||||
nodes.push(content.slice(lastIndex, textEnd));
|
||||
}
|
||||
if (upto === null || entity.end <= upto) {
|
||||
if (entity.type === "mention") {
|
||||
nodes.push(buildMentionNode(entity, i));
|
||||
} else if (entity.type === "tag") {
|
||||
nodes.push(buildTagNode(entity, i));
|
||||
} else if (entity.type === "link") {
|
||||
nodes.push(buildLinkNode(entity, i));
|
||||
}
|
||||
}
|
||||
lastIndex = entity.end;
|
||||
}
|
||||
if (upto === null) {
|
||||
if (lastIndex < content.length) {
|
||||
nodes.push(content.slice(lastIndex));
|
||||
}
|
||||
} else if (lastIndex < upto) {
|
||||
nodes.push(content.slice(lastIndex, upto));
|
||||
}
|
||||
return nodes;
|
||||
};
|
||||
const ellipsisConfig = ellipsis && typeof ellipsis === "object" ? ellipsis : null;
|
||||
const expandedFromProps = ellipsisConfig && typeof ellipsisConfig.expanded === "boolean" ? ellipsisConfig.expanded : void 0;
|
||||
const isExpandedControlled = expandedFromProps !== void 0;
|
||||
const mergedExpanded = isExpandedControlled ? expandedFromProps : expanded;
|
||||
const handleExpand = useCallback(
|
||||
(event) => {
|
||||
if (!isExpandedControlled) {
|
||||
setExpanded(true);
|
||||
}
|
||||
ellipsisConfig?.onExpand?.(event, { expanded: true });
|
||||
},
|
||||
[ellipsisConfig, isExpandedControlled]
|
||||
);
|
||||
useEffect(() => {
|
||||
if (isExpandedControlled) {
|
||||
return;
|
||||
}
|
||||
if (ellipsisConfig && "count" in ellipsisConfig) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
if (content.length <= count && !expanded) {
|
||||
setExpanded(true);
|
||||
}
|
||||
}
|
||||
}, [content.length, ellipsisConfig, expanded, isExpandedControlled]);
|
||||
useEffect(() => {
|
||||
if (mergedExpanded && !viewed && containerInsideView) {
|
||||
setViewed(true);
|
||||
onView?.();
|
||||
}
|
||||
}, [mergedExpanded, viewed, containerInsideView, onView]);
|
||||
useEffect(() => {
|
||||
const ref = containerRef.current;
|
||||
if (!ref) {
|
||||
return;
|
||||
}
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
const entry = entries[0];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
if (entry.isIntersecting && !containerInsideView) {
|
||||
setContainerInsideView(true);
|
||||
}
|
||||
}, { threshold: 0.5 });
|
||||
observer.observe(ref);
|
||||
return () => {
|
||||
observer.unobserve(ref);
|
||||
};
|
||||
}, [containerInsideView]);
|
||||
const mergedStyle = {
|
||||
...baseSelectableStyle,
|
||||
...blur ? blurStyle : void 0,
|
||||
...style
|
||||
};
|
||||
if (ellipsisConfig && "count" in ellipsisConfig) {
|
||||
const { count, expandable } = ellipsisConfig;
|
||||
if (!mergedExpanded && count && content.length > count) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
const truncatedNodes = buildParts(cutoff);
|
||||
return /* @__PURE__ */ jsxs(
|
||||
ParagraphComponent,
|
||||
{
|
||||
ref: containerRef,
|
||||
weight,
|
||||
className: joinClassName(className),
|
||||
style: mergedStyle,
|
||||
...props,
|
||||
children: [
|
||||
truncatedNodes,
|
||||
"\u2026",
|
||||
expandable && /* @__PURE__ */ jsx(
|
||||
ReadMoreButton,
|
||||
{
|
||||
symbol: ellipsisConfig.symbol,
|
||||
onClick: handleExpand,
|
||||
expanded: mergedExpanded,
|
||||
TitleComponent
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
if (ellipsisConfig && "rows" in ellipsisConfig) {
|
||||
const paragraphEllipsis = mergedExpanded ? false : {
|
||||
...ellipsisConfig,
|
||||
symbol: ellipsisConfig.expandable ? /* @__PURE__ */ jsx(
|
||||
ReadMoreButton,
|
||||
{
|
||||
symbol: ellipsisConfig.symbol,
|
||||
onClick: handleExpand,
|
||||
expanded: mergedExpanded,
|
||||
TitleComponent
|
||||
}
|
||||
) : ellipsisConfig.symbol
|
||||
};
|
||||
return /* @__PURE__ */ jsx(
|
||||
ParagraphComponent,
|
||||
{
|
||||
ref: containerRef,
|
||||
weight,
|
||||
className: joinClassName(className),
|
||||
style: mergedStyle,
|
||||
ellipsis: paragraphEllipsis,
|
||||
...props,
|
||||
children: buildParts()
|
||||
}
|
||||
);
|
||||
}
|
||||
return /* @__PURE__ */ jsx(
|
||||
ParagraphComponent,
|
||||
{
|
||||
ref: containerRef,
|
||||
weight,
|
||||
className: joinClassName(className),
|
||||
style: mergedStyle,
|
||||
...props,
|
||||
children: buildParts()
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
ContentText.displayName = "ContentText";
|
||||
var DefaultMention = ({ entity }) => /* @__PURE__ */ jsx(TachTypography.Text.Body, { color: "link", children: entity.displayText });
|
||||
var DefaultTag = ({ entity }) => /* @__PURE__ */ jsx(TachTypography.Text.Body, { color: "link", children: entity.text });
|
||||
var ContentTextWithSuggestions = ({
|
||||
renderMention,
|
||||
renderTag,
|
||||
...props
|
||||
}) => {
|
||||
return /* @__PURE__ */ jsx(
|
||||
ContentText,
|
||||
{
|
||||
...props,
|
||||
renderMention: (entity, index) => renderMention ? renderMention(entity, index) : /* @__PURE__ */ jsx(DefaultMention, { entity }),
|
||||
renderTag: (entity, index) => renderTag ? renderTag(entity, index) : /* @__PURE__ */ jsx(DefaultTag, { entity })
|
||||
}
|
||||
);
|
||||
};
|
||||
var ContentTitleWithSuggestions = ({
|
||||
text,
|
||||
ellipsis,
|
||||
blur = false,
|
||||
...rest
|
||||
}) => {
|
||||
const normalizedEllipsis = ellipsis === void 0 ? { rows: 2 } : ellipsis;
|
||||
const textProps = text === void 0 ? {} : { text };
|
||||
return /* @__PURE__ */ jsx(
|
||||
ContentTextWithSuggestions,
|
||||
{
|
||||
weight: "bold",
|
||||
blur,
|
||||
ellipsis: normalizedEllipsis,
|
||||
...textProps,
|
||||
...rest
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export { ContentText, ContentTextWithSuggestions, ContentTitleWithSuggestions, findAllEntities, findLinks, findMentions, findTags, mentionLinkRegexp, parseMention, processContent };
|
||||
//# sourceMappingURL=index.js.map
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/content-suggestions/dist/react/index.js.map
vendored
Normal file
1
packages/content-suggestions/dist/react/index.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
29
packages/content-suggestions/dist/types-BRt4hd7A.d.cts
vendored
Normal file
29
packages/content-suggestions/dist/types-BRt4hd7A.d.cts
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
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 };
|
||||
29
packages/content-suggestions/dist/types-BRt4hd7A.d.ts
vendored
Normal file
29
packages/content-suggestions/dist/types-BRt4hd7A.d.ts
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
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 };
|
||||
105
packages/content-suggestions/package.json
Normal file
105
packages/content-suggestions/package.json
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"name": "@hublib-web/content-suggestions",
|
||||
"version": "0.1.0",
|
||||
"description": "Content text/title with mentions, tags and links for React and Angular",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"main": "./dist/core/index.cjs",
|
||||
"module": "./dist/core/index.js",
|
||||
"types": "./dist/core/index.d.ts",
|
||||
"sideEffects": false,
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"react": [
|
||||
"dist/react/index.d.ts"
|
||||
],
|
||||
"angular": [
|
||||
"dist/angular/index.d.ts"
|
||||
],
|
||||
"core": [
|
||||
"dist/core/index.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/core/index.d.ts",
|
||||
"import": "./dist/core/index.js",
|
||||
"require": "./dist/core/index.cjs"
|
||||
},
|
||||
"./core": {
|
||||
"types": "./dist/core/index.d.ts",
|
||||
"import": "./dist/core/index.js",
|
||||
"require": "./dist/core/index.cjs"
|
||||
},
|
||||
"./react": {
|
||||
"types": "./dist/react/index.d.ts",
|
||||
"import": "./dist/react/index.js",
|
||||
"require": "./dist/react/index.cjs"
|
||||
},
|
||||
"./angular": {
|
||||
"types": "./dist/angular/index.d.ts",
|
||||
"import": "./dist/angular/index.js",
|
||||
"require": "./dist/angular/index.cjs"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn clean && tsup",
|
||||
"clean": "rm -rf dist storybook-static",
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"test": "vitest run --passWithNoTests",
|
||||
"lint": "eslint src --ext .ts,.tsx",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"storybook:build": "storybook build"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">=17.0.0",
|
||||
"@angular/core": ">=17.0.0",
|
||||
"@hublib-web/tach-typography": ">=0.1.0",
|
||||
"antd": ">=5.0.0",
|
||||
"react": ">=18.0.0",
|
||||
"react-dom": ">=18.0.0",
|
||||
"rxjs": ">=7.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@angular/common": {
|
||||
"optional": true
|
||||
},
|
||||
"@angular/core": {
|
||||
"optional": true
|
||||
},
|
||||
"antd": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"rxjs": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/common": "^20.3.17",
|
||||
"@angular/core": "^20.3.17",
|
||||
"@hublib-web/tach-typography": "workspace:*",
|
||||
"@storybook/addon-essentials": "8.6.14",
|
||||
"@storybook/react": "8.6.14",
|
||||
"@storybook/react-vite": "8.6.14",
|
||||
"@types/react": "^19.2.2",
|
||||
"antd": "^5.29.3",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"rxjs": "^7.8.2",
|
||||
"storybook": "8.6.14",
|
||||
"tsup": "^8.5.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "6.4.1"
|
||||
}
|
||||
}
|
||||
106
packages/content-suggestions/src/angular/index.ts
Normal file
106
packages/content-suggestions/src/angular/index.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import type { ContentEntity, LinkEntity, MentionEntity, TagEntity } from "../core";
|
||||
|
||||
import { findAllEntities } from "../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 const buildAngularTagHref = (entity: TagEntity): string => {
|
||||
return `/search/?query=${encodeURIComponent(entity.tag.toLowerCase())}`;
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
19
packages/content-suggestions/src/core/index.ts
Normal file
19
packages/content-suggestions/src/core/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export type {
|
||||
BaseEntity,
|
||||
ContentEntity,
|
||||
LinkEntity,
|
||||
MentionEntity,
|
||||
ParsedMention,
|
||||
ProcessedContent,
|
||||
TagEntity,
|
||||
} from "./types";
|
||||
|
||||
export {
|
||||
findAllEntities,
|
||||
findLinks,
|
||||
findMentions,
|
||||
findTags,
|
||||
mentionLinkRegexp,
|
||||
parseMention,
|
||||
processContent,
|
||||
} from "./parser";
|
||||
115
packages/content-suggestions/src/core/parser.ts
Normal file
115
packages/content-suggestions/src/core/parser.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import type {
|
||||
ContentEntity,
|
||||
LinkEntity,
|
||||
MentionEntity,
|
||||
ParsedMention,
|
||||
ProcessedContent,
|
||||
TagEntity,
|
||||
} from "./types";
|
||||
|
||||
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: string): ParsedMention | null => {
|
||||
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: string): MentionEntity[] => {
|
||||
let match: RegExpExecArray | null;
|
||||
const matches: MentionEntity[] = [];
|
||||
|
||||
while ((match = mentionLinkRegexp.exec(text)) !== null) {
|
||||
const parsed = parseMention(match[0]);
|
||||
const baseMention: Omit<MentionEntity, "userId"> = {
|
||||
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: string): TagEntity[] => {
|
||||
const regex = /#[^\s]{1,201}/g;
|
||||
const results: TagEntity[] = [];
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
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: string): LinkEntity[] => {
|
||||
const regex =
|
||||
/\b((https?:\/\/)?(?:[\w-]+\.)+[a-z]{2,}(\/[\w\-._~:/?#[\]@!$&'()*+,;=]*)?)/gi;
|
||||
const results: LinkEntity[] = [];
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
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: string): ContentEntity[] => {
|
||||
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: string): ProcessedContent => {
|
||||
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,
|
||||
};
|
||||
};
|
||||
33
packages/content-suggestions/src/core/types.ts
Normal file
33
packages/content-suggestions/src/core/types.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
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[];
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import type { ComponentProps } from "react";
|
||||
import type { MentionEntity, TagEntity } from "../../core";
|
||||
|
||||
import React from "react";
|
||||
|
||||
import { TachTypography } from "@hublib-web/tach-typography/react";
|
||||
import { ContentText } from "./content-text";
|
||||
|
||||
type BaseContentTextProps = Omit<
|
||||
ComponentProps<typeof ContentText>,
|
||||
"renderMention" | "renderTag"
|
||||
> & {
|
||||
renderMention?: (entity: MentionEntity, index: number) => React.ReactNode;
|
||||
renderTag?: (entity: TagEntity, index: number) => React.ReactNode;
|
||||
};
|
||||
|
||||
const DefaultMention = ({ entity }: { entity: MentionEntity }) => (
|
||||
<TachTypography.Text.Body color="link">{entity.displayText}</TachTypography.Text.Body>
|
||||
);
|
||||
|
||||
const DefaultTag = ({ entity }: { entity: TagEntity }) => (
|
||||
<TachTypography.Text.Body color="link">
|
||||
{entity.text}
|
||||
</TachTypography.Text.Body>
|
||||
);
|
||||
|
||||
export const ContentTextWithSuggestions = ({
|
||||
renderMention,
|
||||
renderTag,
|
||||
...props
|
||||
}: BaseContentTextProps) => {
|
||||
return (
|
||||
<ContentText
|
||||
{...props}
|
||||
renderMention={(entity, index) =>
|
||||
renderMention ? renderMention(entity, index) : <DefaultMention entity={entity} />
|
||||
}
|
||||
renderTag={(entity, index) =>
|
||||
renderTag ? (
|
||||
renderTag(entity, index)
|
||||
) : (
|
||||
<DefaultTag entity={entity} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,353 @@
|
||||
import type { ComponentProps, CSSProperties, ReactNode } from "react";
|
||||
import type { EllipsisConfig } from "antd/lib/typography/Base";
|
||||
import type {
|
||||
ContentEntity,
|
||||
LinkEntity,
|
||||
MentionEntity,
|
||||
TagEntity,
|
||||
} from "../../core";
|
||||
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import { TachTypography } from "@hublib-web/tach-typography/react";
|
||||
import { findAllEntities } from "../../core";
|
||||
|
||||
type CustomEllipsisConfig =
|
||||
| ({
|
||||
count: number;
|
||||
rows?: never;
|
||||
expandable?: boolean;
|
||||
} & Partial<EllipsisConfig>)
|
||||
| ({
|
||||
rows: number;
|
||||
count?: never;
|
||||
expandable?: boolean;
|
||||
} & Partial<EllipsisConfig>)
|
||||
| false;
|
||||
|
||||
type ParagraphBodyProps = ComponentProps<typeof TachTypography.Paragraph.Body>;
|
||||
type TextBodyProps = ComponentProps<typeof TachTypography.Text.Body>;
|
||||
type TitleBodyProps = ComponentProps<typeof TachTypography.Title.Body>;
|
||||
|
||||
const joinClassName = (...values: Array<string | false | undefined>) =>
|
||||
values.filter(Boolean).join(" ");
|
||||
|
||||
const baseSelectableStyle: CSSProperties = {
|
||||
whiteSpace: "pre-wrap",
|
||||
WebkitTouchCallout: "default",
|
||||
WebkitUserSelect: "text",
|
||||
KhtmlUserSelect: "text",
|
||||
MozUserSelect: "text",
|
||||
msUserSelect: "text",
|
||||
userSelect: "text",
|
||||
};
|
||||
|
||||
const blurStyle: CSSProperties = {
|
||||
filter: "blur(3px)",
|
||||
WebkitUserSelect: "none",
|
||||
KhtmlUserSelect: "none",
|
||||
MozUserSelect: "none",
|
||||
msUserSelect: "none",
|
||||
userSelect: "none",
|
||||
pointerEvents: "none",
|
||||
};
|
||||
|
||||
const ReadMoreButton: React.FC<{
|
||||
onClick: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
||||
symbol?: EllipsisConfig["symbol"];
|
||||
expanded?: boolean;
|
||||
TitleComponent: React.ComponentType<TitleBodyProps>;
|
||||
}> = ({ onClick, symbol = "Читать полностью", expanded = false, TitleComponent }) => (
|
||||
<TitleComponent
|
||||
onClick={event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onClick(event);
|
||||
}}
|
||||
weight="bold"
|
||||
level={5}
|
||||
>
|
||||
{typeof symbol === "function" ? symbol(expanded) : symbol}
|
||||
</TitleComponent>
|
||||
);
|
||||
|
||||
export interface ContentTextProps {
|
||||
className?: string;
|
||||
weight?: TextBodyProps["weight"];
|
||||
text?: string | null;
|
||||
ellipsis?: CustomEllipsisConfig;
|
||||
blur?: boolean;
|
||||
style?: TextBodyProps["style"];
|
||||
onView?: () => void;
|
||||
renderMention?: (entity: MentionEntity, index: number) => ReactNode;
|
||||
renderTag?: (entity: TagEntity, index: number) => ReactNode;
|
||||
renderLink?: (entity: LinkEntity, index: number) => ReactNode;
|
||||
ParagraphComponent?: React.ComponentType<ParagraphBodyProps>;
|
||||
TextComponent?: React.ComponentType<TextBodyProps>;
|
||||
TitleComponent?: React.ComponentType<TitleBodyProps>;
|
||||
}
|
||||
|
||||
export const ContentText: React.NamedExoticComponent<ContentTextProps> = React.memo(
|
||||
({
|
||||
text,
|
||||
className,
|
||||
ellipsis = false,
|
||||
blur = false,
|
||||
weight = "normal",
|
||||
style,
|
||||
onView,
|
||||
renderMention,
|
||||
renderTag,
|
||||
renderLink,
|
||||
ParagraphComponent = TachTypography.Paragraph.Body,
|
||||
TextComponent = TachTypography.Text.Body,
|
||||
TitleComponent = TachTypography.Title.Body,
|
||||
...props
|
||||
}: ContentTextProps) => {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [containerInsideView, setContainerInsideView] = useState(false);
|
||||
const [viewed, setViewed] = useState(false);
|
||||
const content = text || "";
|
||||
|
||||
const entities = useMemo<ContentEntity[]>(() => findAllEntities(content), [content]);
|
||||
|
||||
const wrapWithKey = (node: ReactNode, key: React.Key) => {
|
||||
if (React.isValidElement(node)) {
|
||||
return React.cloneElement(node, { key });
|
||||
}
|
||||
|
||||
return <React.Fragment key={key}>{node}</React.Fragment>;
|
||||
};
|
||||
|
||||
const buildMentionNode = (entity: MentionEntity, index: number) => {
|
||||
const defaultNode = (
|
||||
<TextComponent color="link" weight={weight}>
|
||||
{entity.displayText}
|
||||
</TextComponent>
|
||||
);
|
||||
const customNode = renderMention?.(entity, index) ?? defaultNode;
|
||||
return wrapWithKey(customNode, `mention-${entity.start}-${index}`);
|
||||
};
|
||||
|
||||
const buildTagNode = (entity: TagEntity, index: number) => {
|
||||
const defaultNode = (
|
||||
<TextComponent color="link" weight={weight}>
|
||||
{entity.text}
|
||||
</TextComponent>
|
||||
);
|
||||
const customNode = renderTag?.(entity, index) ?? defaultNode;
|
||||
return wrapWithKey(customNode, `tag-${entity.start}-${index}`);
|
||||
};
|
||||
|
||||
const buildLinkNode = (entity: LinkEntity, index: number) => {
|
||||
const defaultNode = (
|
||||
<TachTypography.Link.Body
|
||||
target="_blank"
|
||||
referrerPolicy="no-referrer"
|
||||
color="link"
|
||||
weight={weight}
|
||||
href={entity.url}
|
||||
>
|
||||
{entity.text}
|
||||
</TachTypography.Link.Body>
|
||||
);
|
||||
const customNode = renderLink?.(entity, index) ?? defaultNode;
|
||||
return wrapWithKey(customNode, `link-${entity.start}-${index}`);
|
||||
};
|
||||
|
||||
const buildParts = (upto: number | null = null): ReactNode[] => {
|
||||
let lastIndex = 0;
|
||||
const nodes: ReactNode[] = [];
|
||||
|
||||
for (const [i, entity] of entities.entries()) {
|
||||
|
||||
if (upto !== null && entity.start >= upto) {
|
||||
break;
|
||||
}
|
||||
|
||||
const textEnd = upto !== null ? Math.min(entity.start, upto) : entity.start;
|
||||
if (entity.start > lastIndex && lastIndex < textEnd) {
|
||||
nodes.push(content.slice(lastIndex, textEnd));
|
||||
}
|
||||
|
||||
if (upto === null || entity.end <= upto) {
|
||||
if (entity.type === "mention") {
|
||||
nodes.push(buildMentionNode(entity, i));
|
||||
} else if (entity.type === "tag") {
|
||||
nodes.push(buildTagNode(entity, i));
|
||||
} else if (entity.type === "link") {
|
||||
nodes.push(buildLinkNode(entity, i));
|
||||
}
|
||||
}
|
||||
|
||||
lastIndex = entity.end;
|
||||
}
|
||||
|
||||
if (upto === null) {
|
||||
if (lastIndex < content.length) {
|
||||
nodes.push(content.slice(lastIndex));
|
||||
}
|
||||
} else if (lastIndex < upto) {
|
||||
nodes.push(content.slice(lastIndex, upto));
|
||||
}
|
||||
|
||||
return nodes;
|
||||
};
|
||||
|
||||
const ellipsisConfig = ellipsis && typeof ellipsis === "object" ? ellipsis : null;
|
||||
const expandedFromProps =
|
||||
ellipsisConfig && typeof ellipsisConfig.expanded === "boolean"
|
||||
? ellipsisConfig.expanded
|
||||
: undefined;
|
||||
const isExpandedControlled = expandedFromProps !== undefined;
|
||||
const mergedExpanded = isExpandedControlled ? expandedFromProps : expanded;
|
||||
|
||||
const handleExpand = useCallback(
|
||||
(event: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
if (!isExpandedControlled) {
|
||||
setExpanded(true);
|
||||
}
|
||||
|
||||
ellipsisConfig?.onExpand?.(event, { expanded: true });
|
||||
},
|
||||
[ellipsisConfig, isExpandedControlled],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isExpandedControlled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ellipsisConfig && "count" in ellipsisConfig) {
|
||||
const count = ellipsisConfig.count ?? 0;
|
||||
if (content.length <= count && !expanded) {
|
||||
setExpanded(true);
|
||||
}
|
||||
}
|
||||
}, [content.length, ellipsisConfig, expanded, isExpandedControlled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (mergedExpanded && !viewed && containerInsideView) {
|
||||
setViewed(true);
|
||||
onView?.();
|
||||
}
|
||||
}, [mergedExpanded, viewed, containerInsideView, onView]);
|
||||
|
||||
useEffect(() => {
|
||||
const ref = containerRef.current;
|
||||
if (!ref) {
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
const entry = entries[0];
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.isIntersecting && !containerInsideView) {
|
||||
setContainerInsideView(true);
|
||||
}
|
||||
}, { threshold: 0.5 });
|
||||
|
||||
observer.observe(ref);
|
||||
|
||||
return () => {
|
||||
observer.unobserve(ref);
|
||||
};
|
||||
}, [containerInsideView]);
|
||||
|
||||
const mergedStyle: CSSProperties = {
|
||||
...baseSelectableStyle,
|
||||
...(blur ? blurStyle : undefined),
|
||||
...(style as CSSProperties | undefined),
|
||||
};
|
||||
|
||||
if (ellipsisConfig && "count" in ellipsisConfig) {
|
||||
const { count, expandable } = ellipsisConfig;
|
||||
|
||||
if (!mergedExpanded && count && content.length > count) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const truncatedNodes = buildParts(cutoff);
|
||||
|
||||
return (
|
||||
<ParagraphComponent
|
||||
ref={containerRef}
|
||||
weight={weight}
|
||||
className={joinClassName(className)}
|
||||
style={mergedStyle}
|
||||
{...(props as ParagraphBodyProps)}
|
||||
>
|
||||
{truncatedNodes}…
|
||||
{expandable && (
|
||||
<ReadMoreButton
|
||||
symbol={ellipsisConfig.symbol}
|
||||
onClick={handleExpand}
|
||||
expanded={mergedExpanded}
|
||||
TitleComponent={TitleComponent}
|
||||
/>
|
||||
)}
|
||||
</ParagraphComponent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (ellipsisConfig && "rows" in ellipsisConfig) {
|
||||
const paragraphEllipsis = mergedExpanded
|
||||
? false
|
||||
: {
|
||||
...ellipsisConfig,
|
||||
symbol: ellipsisConfig.expandable ? (
|
||||
<ReadMoreButton
|
||||
symbol={ellipsisConfig.symbol}
|
||||
onClick={handleExpand}
|
||||
expanded={mergedExpanded}
|
||||
TitleComponent={TitleComponent}
|
||||
/>
|
||||
) : (
|
||||
ellipsisConfig.symbol
|
||||
),
|
||||
};
|
||||
|
||||
return (
|
||||
<ParagraphComponent
|
||||
ref={containerRef}
|
||||
weight={weight}
|
||||
className={joinClassName(className)}
|
||||
style={mergedStyle}
|
||||
ellipsis={paragraphEllipsis}
|
||||
{...(props as ParagraphBodyProps)}
|
||||
>
|
||||
{buildParts()}
|
||||
</ParagraphComponent>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ParagraphComponent
|
||||
ref={containerRef}
|
||||
weight={weight}
|
||||
className={joinClassName(className)}
|
||||
style={mergedStyle}
|
||||
{...(props as ParagraphBodyProps)}
|
||||
>
|
||||
{buildParts()}
|
||||
</ParagraphComponent>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
ContentText.displayName = "ContentText";
|
||||
@@ -0,0 +1,28 @@
|
||||
import type { ComponentProps } from "react";
|
||||
|
||||
import React from "react";
|
||||
|
||||
import { ContentTextWithSuggestions } from "./content-text-with-suggestions";
|
||||
|
||||
interface ContentTitleWithSuggestionsProps
|
||||
extends Omit<ComponentProps<typeof ContentTextWithSuggestions>, "weight"> {}
|
||||
|
||||
export const ContentTitleWithSuggestions = ({
|
||||
text,
|
||||
ellipsis,
|
||||
blur = false,
|
||||
...rest
|
||||
}: ContentTitleWithSuggestionsProps) => {
|
||||
const normalizedEllipsis = ellipsis === undefined ? { rows: 2 } : ellipsis;
|
||||
const textProps = text === undefined ? {} : { text };
|
||||
|
||||
return (
|
||||
<ContentTextWithSuggestions
|
||||
weight="bold"
|
||||
blur={blur}
|
||||
ellipsis={normalizedEllipsis}
|
||||
{...textProps}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
27
packages/content-suggestions/src/react/index.tsx
Normal file
27
packages/content-suggestions/src/react/index.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
export { ContentText, type ContentTextProps } from "./components/content-text";
|
||||
export {
|
||||
ContentTextWithSuggestions,
|
||||
} from "./components/content-text-with-suggestions";
|
||||
export {
|
||||
ContentTitleWithSuggestions,
|
||||
} from "./components/content-title-with-suggestions";
|
||||
|
||||
export type {
|
||||
BaseEntity,
|
||||
ContentEntity,
|
||||
LinkEntity,
|
||||
MentionEntity,
|
||||
ParsedMention,
|
||||
ProcessedContent,
|
||||
TagEntity,
|
||||
} from "../core";
|
||||
|
||||
export {
|
||||
findAllEntities,
|
||||
findLinks,
|
||||
findMentions,
|
||||
findTags,
|
||||
mentionLinkRegexp,
|
||||
parseMention,
|
||||
processContent,
|
||||
} from "../core";
|
||||
@@ -0,0 +1,77 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import type { MentionEntity, TagEntity } from "../core";
|
||||
import React from "react";
|
||||
|
||||
import { TachTypography } from "@hublib-web/tach-typography/react";
|
||||
import { ContentTextWithSuggestions } from "../react";
|
||||
|
||||
const DEMO_TEXT =
|
||||
"Пример текста с упоминанием @[Иван Петров](123e4567-e89b-12d3-a456-426614174000), тегом #frontend и ссылкой docs.example.com";
|
||||
|
||||
const meta: Meta<typeof ContentTextWithSuggestions> = {
|
||||
title: "React/ContentTextWithSuggestions",
|
||||
component: ContentTextWithSuggestions,
|
||||
tags: ["autodocs"],
|
||||
args: {
|
||||
text: DEMO_TEXT,
|
||||
blur: false,
|
||||
},
|
||||
argTypes: {
|
||||
text: { control: "text" },
|
||||
blur: { control: "boolean" },
|
||||
ellipsis: { control: false },
|
||||
renderMention: { control: false },
|
||||
renderTag: { control: false },
|
||||
renderLink: { control: false },
|
||||
ParagraphComponent: { control: false },
|
||||
TextComponent: { control: false },
|
||||
TitleComponent: { control: false },
|
||||
},
|
||||
render: args => (
|
||||
<div style={{ maxWidth: 780, width: "100%" }}>
|
||||
<ContentTextWithSuggestions {...args} />
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Playground: Story = {};
|
||||
|
||||
export const WithExpandableEllipsis: Story = {
|
||||
args: {
|
||||
ellipsis: {
|
||||
rows: 2,
|
||||
expandable: true,
|
||||
symbol: "Читать полностью",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCustomMentionAndTagRender: Story = {
|
||||
render: args => {
|
||||
const renderMention = (entity: MentionEntity) => (
|
||||
<TachTypography.Link.Body href={`https://example.com/users/${entity.userId ?? "unknown"}`} color="link">
|
||||
{entity.displayText}
|
||||
</TachTypography.Link.Body>
|
||||
);
|
||||
|
||||
const renderTag = (entity: TagEntity) => (
|
||||
<TachTypography.Link.Body href={`https://example.com/tags/${entity.tag}`} color="link">
|
||||
{entity.text}
|
||||
</TachTypography.Link.Body>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: 780, width: "100%" }}>
|
||||
<ContentTextWithSuggestions
|
||||
{...args}
|
||||
renderMention={renderMention}
|
||||
renderTag={renderTag}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import { ContentTitleWithSuggestions } from "../react";
|
||||
|
||||
const DEMO_TITLE =
|
||||
"Заголовок с @[Мария](123e4567-e89b-12d3-a456-426614174001), тегом #release и ссылкой github.com";
|
||||
|
||||
const meta: Meta<typeof ContentTitleWithSuggestions> = {
|
||||
title: "React/ContentTitleWithSuggestions",
|
||||
component: ContentTitleWithSuggestions,
|
||||
tags: ["autodocs"],
|
||||
args: {
|
||||
text: DEMO_TITLE,
|
||||
blur: false,
|
||||
ellipsis: {
|
||||
rows: 2,
|
||||
expandable: true,
|
||||
symbol: "Читать полностью",
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
text: { control: "text" },
|
||||
blur: { control: "boolean" },
|
||||
ellipsis: { control: false },
|
||||
renderMention: { control: false },
|
||||
renderTag: { control: false },
|
||||
renderLink: { control: false },
|
||||
ParagraphComponent: { control: false },
|
||||
TextComponent: { control: false },
|
||||
TitleComponent: { control: false },
|
||||
},
|
||||
render: args => (
|
||||
<div style={{ maxWidth: 780, width: "100%" }}>
|
||||
<ContentTitleWithSuggestions {...args} />
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Playground: Story = {};
|
||||
|
||||
export const Blurred: Story = {
|
||||
args: {
|
||||
blur: true,
|
||||
},
|
||||
};
|
||||
10
packages/content-suggestions/tsconfig.json
Normal file
10
packages/content-suggestions/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"types": ["node", "react"]
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
25
packages/content-suggestions/tsup.config.ts
Normal file
25
packages/content-suggestions/tsup.config.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { defineConfig } from "tsup";
|
||||
|
||||
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,
|
||||
sourcemap: true,
|
||||
clean: false,
|
||||
target: "es2022",
|
||||
minify: false,
|
||||
treeshake: true,
|
||||
splitting: false,
|
||||
external: [
|
||||
"react",
|
||||
"react-dom",
|
||||
"antd",
|
||||
"@angular/core",
|
||||
"@angular/common",
|
||||
"@hublib-web/tach-typography/react",
|
||||
],
|
||||
});
|
||||
22
packages/tach-typography/.storybook/main.ts
Normal file
22
packages/tach-typography/.storybook/main.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { StorybookConfig } from "@storybook/react-vite";
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ["../src/**/*.stories.@(ts|tsx)"],
|
||||
addons: [
|
||||
"@storybook/addon-essentials",
|
||||
"@storybook/addon-interactions",
|
||||
"@storybook/addon-a11y",
|
||||
],
|
||||
framework: {
|
||||
name: "@storybook/react-vite",
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: "tag",
|
||||
},
|
||||
core: {
|
||||
disableTelemetry: true,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
90
packages/tach-typography/.storybook/preview.css
Normal file
90
packages/tach-typography/.storybook/preview.css
Normal file
@@ -0,0 +1,90 @@
|
||||
:root {
|
||||
--Text-Primary: #0f172a;
|
||||
--Text-Secondary: #334155;
|
||||
--Text-Tertiary: #64748b;
|
||||
--Text-Quaternary: #94a3b8;
|
||||
--System-HashtagsInPost: #2563eb;
|
||||
--Default-White: #ffffff;
|
||||
--Default-Dark: #020617;
|
||||
--System-Alert: #dc2626;
|
||||
--Accent-Malahit: #16a34a;
|
||||
--System-Attantion: #d97706;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Inter, "Segoe UI", sans-serif;
|
||||
}
|
||||
|
||||
.sb-show-main {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.tach-story-surface {
|
||||
min-width: 320px;
|
||||
max-width: 920px;
|
||||
padding: 20px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid #dbe1ea;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.tach-story-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tach-story-grid {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tach-story-grid--colors {
|
||||
grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
|
||||
}
|
||||
|
||||
.tach-story-row {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
grid-template-columns: 220px minmax(0, 1fr);
|
||||
align-items: center;
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #e2e8f0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.tach-story-label {
|
||||
color: #475569;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tach-story-token-list {
|
||||
margin: 0;
|
||||
padding-left: 18px;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.tach-story-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.tach-story-table th,
|
||||
.tach-story-table td {
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #e2e8f0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tach-story-table thead {
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.tach-story-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
27
packages/tach-typography/.storybook/preview.ts
Normal file
27
packages/tach-typography/.storybook/preview.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { Preview } from "@storybook/react";
|
||||
|
||||
import "antd/dist/reset.css";
|
||||
import "../src/styles/tach-typography.css";
|
||||
import "./preview.css";
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
controls: {
|
||||
expanded: true,
|
||||
sort: "requiredFirst",
|
||||
},
|
||||
actions: {
|
||||
argTypesRegex: "^on[A-Z].*",
|
||||
},
|
||||
backgrounds: {
|
||||
default: "surface",
|
||||
values: [
|
||||
{ name: "surface", value: "#F5F7FB" },
|
||||
{ name: "dark", value: "#0F172A" },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
||||
98
packages/tach-typography/README.md
Normal file
98
packages/tach-typography/README.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# @hublib-web/tach-typography
|
||||
|
||||
Typography package with shared tokens and framework adapters:
|
||||
|
||||
- `react` adapter based on `antd/Typography`
|
||||
- `angular` adapter based on `ng-zorro-antd/typography`
|
||||
|
||||
## 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"
|
||||
```
|
||||
|
||||
## Install inside this monorepo
|
||||
|
||||
```bash
|
||||
yarn add @hublib-web/tach-typography
|
||||
```
|
||||
|
||||
## Release this package
|
||||
|
||||
1. Bump `version` in `packages/tach-typography/package.json`.
|
||||
2. Build package artifacts:
|
||||
|
||||
```bash
|
||||
yarn workspace @hublib-web/tach-typography build
|
||||
```
|
||||
|
||||
3. Commit release files:
|
||||
|
||||
```bash
|
||||
git add packages/tach-typography/package.json packages/tach-typography/dist
|
||||
git commit -m "release(tach-typography): v0.1.0"
|
||||
```
|
||||
|
||||
4. Create and push tag:
|
||||
|
||||
```bash
|
||||
git tag -a tach-typography-v0.1.0 -m "@hublib-web/tach-typography v0.1.0"
|
||||
git push origin main --follow-tags
|
||||
```
|
||||
|
||||
Detailed docs:
|
||||
|
||||
- [Release policy](../../docs/release-policy.md)
|
||||
- [Git installation](../../docs/git-installation.md)
|
||||
|
||||
## React usage (Ant Design)
|
||||
|
||||
```tsx
|
||||
import "@hublib-web/tach-typography/styles.css";
|
||||
import { TachTypography } from "@hublib-web/tach-typography/react";
|
||||
|
||||
export const Example = () => (
|
||||
<TachTypography.Text.Body color="link" weight="bold" ellipsis={{ rows: 1 }}>
|
||||
Hello from React + AntD
|
||||
</TachTypography.Text.Body>
|
||||
);
|
||||
```
|
||||
|
||||
## Angular usage (NG-ZORRO)
|
||||
|
||||
```ts
|
||||
import { Component } from "@angular/core";
|
||||
import { TachTypographyDirective, TachTypographyNzModule } from "@hublib-web/tach-typography/angular";
|
||||
|
||||
@Component({
|
||||
selector: "app-example",
|
||||
standalone: true,
|
||||
imports: [TachTypographyNzModule, TachTypographyDirective],
|
||||
template: `
|
||||
<span
|
||||
nz-typography
|
||||
tachTypography
|
||||
tachTypography="Body"
|
||||
tachTypographyColor="link"
|
||||
tachTypographyWeight="bold"
|
||||
>
|
||||
Hello from Angular + NG-ZORRO
|
||||
</span>
|
||||
`,
|
||||
})
|
||||
export class ExampleComponent {}
|
||||
```
|
||||
|
||||
## Storybook (dev/design system)
|
||||
|
||||
Run from repository root:
|
||||
|
||||
```bash
|
||||
yarn workspace @hublib-web/tach-typography storybook
|
||||
```
|
||||
|
||||
Build static Storybook:
|
||||
|
||||
```bash
|
||||
yarn workspace @hublib-web/tach-typography storybook:build
|
||||
```
|
||||
204
packages/tach-typography/dist/angular/index.cjs
vendored
Normal file
204
packages/tach-typography/dist/angular/index.cjs
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
'use strict';
|
||||
|
||||
var core = require('@angular/core');
|
||||
var typography = require('ng-zorro-antd/typography');
|
||||
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
||||
var __typeError = (msg) => {
|
||||
throw TypeError(msg);
|
||||
};
|
||||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||||
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
||||
var __decoratorStart = (base) => [, , , __create(null)];
|
||||
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
||||
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
||||
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
|
||||
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
||||
var __runInitializers = (array, flags, self, value) => {
|
||||
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
||||
return value;
|
||||
};
|
||||
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
||||
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
||||
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
||||
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
||||
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
|
||||
return __privateGet(this, extra);
|
||||
}, set [name](x) {
|
||||
return __privateSet(this, extra, x);
|
||||
} }, name));
|
||||
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
||||
for (var i = decorators.length - 1; i >= 0; i--) {
|
||||
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
||||
if (k) {
|
||||
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
||||
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
||||
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
||||
}
|
||||
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
|
||||
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
||||
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
||||
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
||||
}
|
||||
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
||||
};
|
||||
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||||
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
||||
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
||||
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
||||
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
||||
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
||||
|
||||
// src/core/classnames.ts
|
||||
var BASE_CLASS = "tach-typography";
|
||||
var join = (...parts) => parts.filter(Boolean).join(" ");
|
||||
var 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
|
||||
);
|
||||
};
|
||||
var tachTypographyClassList = (options = {}) => {
|
||||
return tachTypographyClassName(options).split(" ").filter(Boolean);
|
||||
};
|
||||
|
||||
// src/core/ellipsis.ts
|
||||
var tachTypographyEllipsisStyle = (ellipsis) => {
|
||||
if (!ellipsis) {
|
||||
return void 0;
|
||||
}
|
||||
const rows = typeof ellipsis === "object" ? ellipsis.rows ?? 1 : 1;
|
||||
return {
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
display: "-webkit-box",
|
||||
WebkitBoxOrient: "vertical",
|
||||
WebkitLineClamp: rows
|
||||
};
|
||||
};
|
||||
|
||||
// src/angular/index.ts
|
||||
var camelToKebab = (value) => value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
||||
var toCssProperty = (styleKey) => {
|
||||
if (styleKey.startsWith("Webkit")) {
|
||||
return `-webkit-${camelToKebab(styleKey.slice(6))}`;
|
||||
}
|
||||
return camelToKebab(styleKey);
|
||||
};
|
||||
var tachAngularTypographyClassName = (options = {}) => {
|
||||
return tachTypographyClassName(options);
|
||||
};
|
||||
var tachAngularTypographyClassList = (options = {}) => {
|
||||
return tachTypographyClassList(options);
|
||||
};
|
||||
var tachAngularTypographyStyles = (ellipsis, preserveStyle = {}) => {
|
||||
const ellipsisStyle = tachTypographyEllipsisStyle(ellipsis);
|
||||
if (!ellipsisStyle) {
|
||||
return preserveStyle;
|
||||
}
|
||||
return {
|
||||
...ellipsisStyle,
|
||||
...preserveStyle
|
||||
};
|
||||
};
|
||||
var _tachTypographyEllipsis_dec, _tachTypographyClassName_dec, _tachTypographyClickable_dec, _tachTypographyWeight_dec, _tachTypographyColor_dec, _tachTypographyVariant_dec, _tachTypography_dec, _TachTypographyDirective_decorators, _init;
|
||||
_TachTypographyDirective_decorators = [core.Directive({
|
||||
selector: "[tachTypography]",
|
||||
standalone: true
|
||||
})], _tachTypography_dec = [core.Input()], _tachTypographyVariant_dec = [core.Input()], _tachTypographyColor_dec = [core.Input()], _tachTypographyWeight_dec = [core.Input()], _tachTypographyClickable_dec = [core.Input()], _tachTypographyClassName_dec = [core.Input()], _tachTypographyEllipsis_dec = [core.Input()];
|
||||
exports.TachTypographyDirective = class TachTypographyDirective {
|
||||
constructor(elementRef, renderer) {
|
||||
this.elementRef = elementRef;
|
||||
this.renderer = renderer;
|
||||
__publicField(this, "tachTypography", __runInitializers(_init, 8, this)), __runInitializers(_init, 11, this);
|
||||
__publicField(this, "tachTypographyVariant", __runInitializers(_init, 12, this, "Body")), __runInitializers(_init, 15, this);
|
||||
__publicField(this, "tachTypographyColor", __runInitializers(_init, 16, this, "primary")), __runInitializers(_init, 19, this);
|
||||
__publicField(this, "tachTypographyWeight", __runInitializers(_init, 20, this, "normal")), __runInitializers(_init, 23, this);
|
||||
__publicField(this, "tachTypographyClickable", __runInitializers(_init, 24, this, false)), __runInitializers(_init, 27, this);
|
||||
__publicField(this, "tachTypographyClassName", __runInitializers(_init, 28, this)), __runInitializers(_init, 31, this);
|
||||
__publicField(this, "tachTypographyEllipsis", __runInitializers(_init, 32, this)), __runInitializers(_init, 35, this);
|
||||
__publicField(this, "appliedClasses", /* @__PURE__ */ new Set());
|
||||
__publicField(this, "appliedStyleProperties", /* @__PURE__ */ new Set());
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
_init = __decoratorStart();
|
||||
__decorateElement(_init, 5, "tachTypography", _tachTypography_dec, exports.TachTypographyDirective);
|
||||
__decorateElement(_init, 5, "tachTypographyVariant", _tachTypographyVariant_dec, exports.TachTypographyDirective);
|
||||
__decorateElement(_init, 5, "tachTypographyColor", _tachTypographyColor_dec, exports.TachTypographyDirective);
|
||||
__decorateElement(_init, 5, "tachTypographyWeight", _tachTypographyWeight_dec, exports.TachTypographyDirective);
|
||||
__decorateElement(_init, 5, "tachTypographyClickable", _tachTypographyClickable_dec, exports.TachTypographyDirective);
|
||||
__decorateElement(_init, 5, "tachTypographyClassName", _tachTypographyClassName_dec, exports.TachTypographyDirective);
|
||||
__decorateElement(_init, 5, "tachTypographyEllipsis", _tachTypographyEllipsis_dec, exports.TachTypographyDirective);
|
||||
exports.TachTypographyDirective = __decorateElement(_init, 0, "TachTypographyDirective", _TachTypographyDirective_decorators, exports.TachTypographyDirective);
|
||||
__runInitializers(_init, 1, exports.TachTypographyDirective);
|
||||
var _TachTypographyNzModule_decorators, _init2;
|
||||
_TachTypographyNzModule_decorators = [core.NgModule({
|
||||
imports: [typography.NzTypographyModule, exports.TachTypographyDirective],
|
||||
exports: [typography.NzTypographyModule, exports.TachTypographyDirective]
|
||||
})];
|
||||
exports.TachTypographyNzModule = class TachTypographyNzModule {
|
||||
};
|
||||
_init2 = __decoratorStart();
|
||||
exports.TachTypographyNzModule = __decorateElement(_init2, 0, "TachTypographyNzModule", _TachTypographyNzModule_decorators, exports.TachTypographyNzModule);
|
||||
__runInitializers(_init2, 1, exports.TachTypographyNzModule);
|
||||
|
||||
exports.tachAngularTypographyClassList = tachAngularTypographyClassList;
|
||||
exports.tachAngularTypographyClassName = tachAngularTypographyClassName;
|
||||
exports.tachAngularTypographyStyles = tachAngularTypographyStyles;
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
1
packages/tach-typography/dist/angular/index.cjs.map
vendored
Normal file
1
packages/tach-typography/dist/angular/index.cjs.map
vendored
Normal file
File diff suppressed because one or more lines are too long
31
packages/tach-typography/dist/angular/index.d.cts
vendored
Normal file
31
packages/tach-typography/dist/angular/index.d.cts
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import { OnChanges, ElementRef, Renderer2, SimpleChanges } from '@angular/core';
|
||||
import { T as TypographyClassOptions, d as TypographyRenderOptions, e as TypographyVariant, c as TypographyColor, f as TypographyWeight, E as EllipsisOptions } from '../types-CQyFuLqp.cjs';
|
||||
|
||||
type AngularTypographyClassInput = TypographyClassOptions;
|
||||
interface AngularTypographyRenderOptions extends TypographyRenderOptions {
|
||||
preserveStyle?: Record<string, string | number>;
|
||||
}
|
||||
declare const tachAngularTypographyClassName: (options?: AngularTypographyClassInput) => string;
|
||||
declare const tachAngularTypographyClassList: (options?: AngularTypographyClassInput) => string[];
|
||||
declare const tachAngularTypographyStyles: (ellipsis?: EllipsisOptions, preserveStyle?: Record<string, string | number>) => Record<string, string | number>;
|
||||
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;
|
||||
}
|
||||
declare class TachTypographyNzModule {
|
||||
}
|
||||
|
||||
export { type AngularTypographyClassInput, type AngularTypographyRenderOptions, TachTypographyDirective, TachTypographyNzModule, tachAngularTypographyClassList, tachAngularTypographyClassName, tachAngularTypographyStyles };
|
||||
31
packages/tach-typography/dist/angular/index.d.ts
vendored
Normal file
31
packages/tach-typography/dist/angular/index.d.ts
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import { OnChanges, ElementRef, Renderer2, SimpleChanges } from '@angular/core';
|
||||
import { T as TypographyClassOptions, d as TypographyRenderOptions, e as TypographyVariant, c as TypographyColor, f as TypographyWeight, E as EllipsisOptions } from '../types-CQyFuLqp.js';
|
||||
|
||||
type AngularTypographyClassInput = TypographyClassOptions;
|
||||
interface AngularTypographyRenderOptions extends TypographyRenderOptions {
|
||||
preserveStyle?: Record<string, string | number>;
|
||||
}
|
||||
declare const tachAngularTypographyClassName: (options?: AngularTypographyClassInput) => string;
|
||||
declare const tachAngularTypographyClassList: (options?: AngularTypographyClassInput) => string[];
|
||||
declare const tachAngularTypographyStyles: (ellipsis?: EllipsisOptions, preserveStyle?: Record<string, string | number>) => Record<string, string | number>;
|
||||
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;
|
||||
}
|
||||
declare class TachTypographyNzModule {
|
||||
}
|
||||
|
||||
export { type AngularTypographyClassInput, type AngularTypographyRenderOptions, TachTypographyDirective, TachTypographyNzModule, tachAngularTypographyClassList, tachAngularTypographyClassName, tachAngularTypographyStyles };
|
||||
200
packages/tach-typography/dist/angular/index.js
vendored
Normal file
200
packages/tach-typography/dist/angular/index.js
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
import { Directive, Input, NgModule } from '@angular/core';
|
||||
import { NzTypographyModule } from 'ng-zorro-antd/typography';
|
||||
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
||||
var __typeError = (msg) => {
|
||||
throw TypeError(msg);
|
||||
};
|
||||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||||
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
||||
var __decoratorStart = (base) => [, , , __create(null)];
|
||||
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
||||
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
||||
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
|
||||
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
||||
var __runInitializers = (array, flags, self, value) => {
|
||||
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
||||
return value;
|
||||
};
|
||||
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
||||
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
||||
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
||||
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
||||
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
|
||||
return __privateGet(this, extra);
|
||||
}, set [name](x) {
|
||||
return __privateSet(this, extra, x);
|
||||
} }, name));
|
||||
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
||||
for (var i = decorators.length - 1; i >= 0; i--) {
|
||||
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
||||
if (k) {
|
||||
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
|
||||
if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
||||
if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
||||
}
|
||||
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
|
||||
if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
||||
else if (typeof it !== "object" || it === null) __typeError("Object expected");
|
||||
else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
||||
}
|
||||
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
||||
};
|
||||
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||||
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
||||
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
||||
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
||||
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
||||
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
||||
|
||||
// src/core/classnames.ts
|
||||
var BASE_CLASS = "tach-typography";
|
||||
var join = (...parts) => parts.filter(Boolean).join(" ");
|
||||
var 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
|
||||
);
|
||||
};
|
||||
var tachTypographyClassList = (options = {}) => {
|
||||
return tachTypographyClassName(options).split(" ").filter(Boolean);
|
||||
};
|
||||
|
||||
// src/core/ellipsis.ts
|
||||
var tachTypographyEllipsisStyle = (ellipsis) => {
|
||||
if (!ellipsis) {
|
||||
return void 0;
|
||||
}
|
||||
const rows = typeof ellipsis === "object" ? ellipsis.rows ?? 1 : 1;
|
||||
return {
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
display: "-webkit-box",
|
||||
WebkitBoxOrient: "vertical",
|
||||
WebkitLineClamp: rows
|
||||
};
|
||||
};
|
||||
|
||||
// src/angular/index.ts
|
||||
var camelToKebab = (value) => value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
||||
var toCssProperty = (styleKey) => {
|
||||
if (styleKey.startsWith("Webkit")) {
|
||||
return `-webkit-${camelToKebab(styleKey.slice(6))}`;
|
||||
}
|
||||
return camelToKebab(styleKey);
|
||||
};
|
||||
var tachAngularTypographyClassName = (options = {}) => {
|
||||
return tachTypographyClassName(options);
|
||||
};
|
||||
var tachAngularTypographyClassList = (options = {}) => {
|
||||
return tachTypographyClassList(options);
|
||||
};
|
||||
var tachAngularTypographyStyles = (ellipsis, preserveStyle = {}) => {
|
||||
const ellipsisStyle = tachTypographyEllipsisStyle(ellipsis);
|
||||
if (!ellipsisStyle) {
|
||||
return preserveStyle;
|
||||
}
|
||||
return {
|
||||
...ellipsisStyle,
|
||||
...preserveStyle
|
||||
};
|
||||
};
|
||||
var _tachTypographyEllipsis_dec, _tachTypographyClassName_dec, _tachTypographyClickable_dec, _tachTypographyWeight_dec, _tachTypographyColor_dec, _tachTypographyVariant_dec, _tachTypography_dec, _TachTypographyDirective_decorators, _init;
|
||||
_TachTypographyDirective_decorators = [Directive({
|
||||
selector: "[tachTypography]",
|
||||
standalone: true
|
||||
})], _tachTypography_dec = [Input()], _tachTypographyVariant_dec = [Input()], _tachTypographyColor_dec = [Input()], _tachTypographyWeight_dec = [Input()], _tachTypographyClickable_dec = [Input()], _tachTypographyClassName_dec = [Input()], _tachTypographyEllipsis_dec = [Input()];
|
||||
var TachTypographyDirective = class {
|
||||
constructor(elementRef, renderer) {
|
||||
this.elementRef = elementRef;
|
||||
this.renderer = renderer;
|
||||
__publicField(this, "tachTypography", __runInitializers(_init, 8, this)), __runInitializers(_init, 11, this);
|
||||
__publicField(this, "tachTypographyVariant", __runInitializers(_init, 12, this, "Body")), __runInitializers(_init, 15, this);
|
||||
__publicField(this, "tachTypographyColor", __runInitializers(_init, 16, this, "primary")), __runInitializers(_init, 19, this);
|
||||
__publicField(this, "tachTypographyWeight", __runInitializers(_init, 20, this, "normal")), __runInitializers(_init, 23, this);
|
||||
__publicField(this, "tachTypographyClickable", __runInitializers(_init, 24, this, false)), __runInitializers(_init, 27, this);
|
||||
__publicField(this, "tachTypographyClassName", __runInitializers(_init, 28, this)), __runInitializers(_init, 31, this);
|
||||
__publicField(this, "tachTypographyEllipsis", __runInitializers(_init, 32, this)), __runInitializers(_init, 35, this);
|
||||
__publicField(this, "appliedClasses", /* @__PURE__ */ new Set());
|
||||
__publicField(this, "appliedStyleProperties", /* @__PURE__ */ new Set());
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
_init = __decoratorStart();
|
||||
__decorateElement(_init, 5, "tachTypography", _tachTypography_dec, TachTypographyDirective);
|
||||
__decorateElement(_init, 5, "tachTypographyVariant", _tachTypographyVariant_dec, TachTypographyDirective);
|
||||
__decorateElement(_init, 5, "tachTypographyColor", _tachTypographyColor_dec, TachTypographyDirective);
|
||||
__decorateElement(_init, 5, "tachTypographyWeight", _tachTypographyWeight_dec, TachTypographyDirective);
|
||||
__decorateElement(_init, 5, "tachTypographyClickable", _tachTypographyClickable_dec, TachTypographyDirective);
|
||||
__decorateElement(_init, 5, "tachTypographyClassName", _tachTypographyClassName_dec, TachTypographyDirective);
|
||||
__decorateElement(_init, 5, "tachTypographyEllipsis", _tachTypographyEllipsis_dec, TachTypographyDirective);
|
||||
TachTypographyDirective = __decorateElement(_init, 0, "TachTypographyDirective", _TachTypographyDirective_decorators, TachTypographyDirective);
|
||||
__runInitializers(_init, 1, TachTypographyDirective);
|
||||
var _TachTypographyNzModule_decorators, _init2;
|
||||
_TachTypographyNzModule_decorators = [NgModule({
|
||||
imports: [NzTypographyModule, TachTypographyDirective],
|
||||
exports: [NzTypographyModule, TachTypographyDirective]
|
||||
})];
|
||||
var TachTypographyNzModule = class {
|
||||
};
|
||||
_init2 = __decoratorStart();
|
||||
TachTypographyNzModule = __decorateElement(_init2, 0, "TachTypographyNzModule", _TachTypographyNzModule_decorators, TachTypographyNzModule);
|
||||
__runInitializers(_init2, 1, TachTypographyNzModule);
|
||||
|
||||
export { TachTypographyDirective, TachTypographyNzModule, tachAngularTypographyClassList, tachAngularTypographyClassName, tachAngularTypographyStyles };
|
||||
//# sourceMappingURL=index.js.map
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/tach-typography/dist/angular/index.js.map
vendored
Normal file
1
packages/tach-typography/dist/angular/index.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
87
packages/tach-typography/dist/core/index.cjs
vendored
Normal file
87
packages/tach-typography/dist/core/index.cjs
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
|
||||
// src/core/types.ts
|
||||
var TYPOGRAPHY_VARIANTS = [
|
||||
"LargeTitle",
|
||||
"Title1",
|
||||
"Title2",
|
||||
"Title3",
|
||||
"Headline",
|
||||
"Body",
|
||||
"Inputs",
|
||||
"Subheadline",
|
||||
"FootnoteUnderline",
|
||||
"Footnote",
|
||||
"Caption",
|
||||
"Caption2",
|
||||
"AccentH1",
|
||||
"AccentH2",
|
||||
"AccentSubttl",
|
||||
"AccentSubttl2",
|
||||
"AccentCaption",
|
||||
"AccentCaption2",
|
||||
"AccentRegularM",
|
||||
"AccentRegularS",
|
||||
"AccentLargeTtl",
|
||||
"AppMediumBody",
|
||||
"AppMediumSubtext",
|
||||
"AppMediumSubtextUnderline"
|
||||
];
|
||||
var TYPOGRAPHY_COLORS = [
|
||||
"primary",
|
||||
"secondary",
|
||||
"tertiary",
|
||||
"quaternary",
|
||||
"link",
|
||||
"white",
|
||||
"dark",
|
||||
"alert",
|
||||
"malahit",
|
||||
"attantion"
|
||||
];
|
||||
|
||||
// src/core/classnames.ts
|
||||
var BASE_CLASS = "tach-typography";
|
||||
var join = (...parts) => parts.filter(Boolean).join(" ");
|
||||
var 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
|
||||
);
|
||||
};
|
||||
var tachTypographyClassList = (options = {}) => {
|
||||
return tachTypographyClassName(options).split(" ").filter(Boolean);
|
||||
};
|
||||
|
||||
// src/core/ellipsis.ts
|
||||
var tachTypographyEllipsisStyle = (ellipsis) => {
|
||||
if (!ellipsis) {
|
||||
return void 0;
|
||||
}
|
||||
const rows = typeof ellipsis === "object" ? ellipsis.rows ?? 1 : 1;
|
||||
return {
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
display: "-webkit-box",
|
||||
WebkitBoxOrient: "vertical",
|
||||
WebkitLineClamp: rows
|
||||
};
|
||||
};
|
||||
|
||||
exports.TYPOGRAPHY_COLORS = TYPOGRAPHY_COLORS;
|
||||
exports.TYPOGRAPHY_VARIANTS = TYPOGRAPHY_VARIANTS;
|
||||
exports.tachTypographyClassList = tachTypographyClassList;
|
||||
exports.tachTypographyClassName = tachTypographyClassName;
|
||||
exports.tachTypographyEllipsisStyle = tachTypographyEllipsisStyle;
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
1
packages/tach-typography/dist/core/index.cjs.map
vendored
Normal file
1
packages/tach-typography/dist/core/index.cjs.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/core/types.ts","../../src/core/classnames.ts","../../src/core/ellipsis.ts"],"names":[],"mappings":";;;AAAO,IAAM,mBAAA,GAAsB;AAAA,EACjC,YAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,aAAA;AAAA,EACA,mBAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA,EACA,cAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA;AAAA,EACA,gBAAA;AAAA,EACA,gBAAA;AAAA,EACA,gBAAA;AAAA,EACA,gBAAA;AAAA,EACA,eAAA;AAAA,EACA,kBAAA;AAAA,EACA;AACF;AAEO,IAAM,iBAAA,GAAoB;AAAA,EAC/B,SAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,YAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF;;;ACpCA,IAAM,UAAA,GAAa,iBAAA;AAEnB,IAAM,IAAA,GAAO,IAAI,KAAA,KACf,KAAA,CAAM,OAAO,OAAO,CAAA,CAAE,KAAK,GAAG,CAAA;AAEzB,IAAM,0BAA0B,CAAC;AAAA,EACtC,OAAA,GAAU,MAAA;AAAA,EACV,KAAA,GAAQ,SAAA;AAAA,EACR,MAAA,GAAS,QAAA;AAAA,EACT,SAAA,GAAY,KAAA;AAAA,EACZ;AACF,CAAA,GAA4B,EAAC,KAAc;AACzC,EAAA,OAAO,IAAA;AAAA,IACL,UAAA;AAAA,IACA,CAAA,EAAG,UAAU,CAAA,EAAA,EAAK,OAAO,CAAA,CAAA;AAAA,IACzB,CAAA,EAAG,UAAU,CAAA,QAAA,EAAW,KAAK,CAAA,CAAA;AAAA,IAC7B,MAAA,KAAW,MAAA,IAAU,CAAA,EAAG,UAAU,CAAA,MAAA,CAAA;AAAA,IAClC,SAAA,IAAa,GAAG,UAAU,CAAA,SAAA,CAAA;AAAA,IAC1B;AAAA,GACF;AACF;AAEO,IAAM,uBAAA,GAA0B,CAAC,OAAA,GAAkC,EAAC,KAAgB;AACzF,EAAA,OAAO,wBAAwB,OAAO,CAAA,CACnC,MAAM,GAAG,CAAA,CACT,OAAO,OAAO,CAAA;AACnB;;;ACxBO,IAAM,2BAAA,GAA8B,CACzC,QAAA,KAC4B;AAC5B,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAAO,OAAO,QAAA,KAAa,QAAA,GAAW,QAAA,CAAS,QAAQ,CAAA,GAAI,CAAA;AAEjE,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,QAAA;AAAA,IACV,YAAA,EAAc,UAAA;AAAA,IACd,OAAA,EAAS,aAAA;AAAA,IACT,eAAA,EAAiB,UAAA;AAAA,IACjB,eAAA,EAAiB;AAAA,GACnB;AACF","file":"index.cjs","sourcesContent":["export const TYPOGRAPHY_VARIANTS = [\n \"LargeTitle\",\n \"Title1\",\n \"Title2\",\n \"Title3\",\n \"Headline\",\n \"Body\",\n \"Inputs\",\n \"Subheadline\",\n \"FootnoteUnderline\",\n \"Footnote\",\n \"Caption\",\n \"Caption2\",\n \"AccentH1\",\n \"AccentH2\",\n \"AccentSubttl\",\n \"AccentSubttl2\",\n \"AccentCaption\",\n \"AccentCaption2\",\n \"AccentRegularM\",\n \"AccentRegularS\",\n \"AccentLargeTtl\",\n \"AppMediumBody\",\n \"AppMediumSubtext\",\n \"AppMediumSubtextUnderline\",\n] as const;\n\nexport const TYPOGRAPHY_COLORS = [\n \"primary\",\n \"secondary\",\n \"tertiary\",\n \"quaternary\",\n \"link\",\n \"white\",\n \"dark\",\n \"alert\",\n \"malahit\",\n \"attantion\",\n] as const;\n\nexport type TypographyVariant = (typeof TYPOGRAPHY_VARIANTS)[number];\nexport type TypographyColor = (typeof TYPOGRAPHY_COLORS)[number];\nexport type TypographyWeight = \"normal\" | \"bold\";\n\nexport interface TypographyClassOptions {\n variant?: TypographyVariant;\n color?: TypographyColor;\n weight?: TypographyWeight;\n clickable?: boolean;\n className?: string | undefined;\n}\n\nexport type EllipsisOptions =\n | boolean\n | {\n rows?: number;\n };\n\nexport interface TypographyRenderOptions extends TypographyClassOptions {\n ellipsis?: EllipsisOptions;\n}\n","import type { TypographyClassOptions } from \"./types\";\n\nconst BASE_CLASS = \"tach-typography\";\n\nconst join = (...parts: Array<string | undefined | null | false>): string =>\n parts.filter(Boolean).join(\" \");\n\nexport const tachTypographyClassName = ({\n variant = \"Body\",\n color = \"primary\",\n weight = \"normal\",\n clickable = false,\n className,\n}: TypographyClassOptions = {}): string => {\n return join(\n BASE_CLASS,\n `${BASE_CLASS}--${variant}`,\n `${BASE_CLASS}--color-${color}`,\n weight === \"bold\" && `${BASE_CLASS}--bold`,\n clickable && `${BASE_CLASS}--pointer`,\n className,\n );\n};\n\nexport const tachTypographyClassList = (options: TypographyClassOptions = {}): string[] => {\n return tachTypographyClassName(options)\n .split(\" \")\n .filter(Boolean);\n};\n","import type { EllipsisOptions } from \"./types\";\n\ntype StyleRecord = Record<string, string | number>;\n\nexport const tachTypographyEllipsisStyle = (\n ellipsis?: EllipsisOptions,\n): StyleRecord | undefined => {\n if (!ellipsis) {\n return undefined;\n }\n\n const rows = typeof ellipsis === \"object\" ? ellipsis.rows ?? 1 : 1;\n\n return {\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n display: \"-webkit-box\",\n WebkitBoxOrient: \"vertical\",\n WebkitLineClamp: rows,\n };\n};\n"]}
|
||||
10
packages/tach-typography/dist/core/index.d.cts
vendored
Normal file
10
packages/tach-typography/dist/core/index.d.cts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { T as TypographyClassOptions, E as EllipsisOptions } from '../types-CQyFuLqp.cjs';
|
||||
export { a as TYPOGRAPHY_COLORS, b as TYPOGRAPHY_VARIANTS, c as TypographyColor, d as TypographyRenderOptions, e as TypographyVariant, f as TypographyWeight } from '../types-CQyFuLqp.cjs';
|
||||
|
||||
declare const tachTypographyClassName: ({ variant, color, weight, clickable, className, }?: TypographyClassOptions) => string;
|
||||
declare const tachTypographyClassList: (options?: TypographyClassOptions) => string[];
|
||||
|
||||
type StyleRecord = Record<string, string | number>;
|
||||
declare const tachTypographyEllipsisStyle: (ellipsis?: EllipsisOptions) => StyleRecord | undefined;
|
||||
|
||||
export { EllipsisOptions, TypographyClassOptions, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle };
|
||||
10
packages/tach-typography/dist/core/index.d.ts
vendored
Normal file
10
packages/tach-typography/dist/core/index.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { T as TypographyClassOptions, E as EllipsisOptions } from '../types-CQyFuLqp.js';
|
||||
export { a as TYPOGRAPHY_COLORS, b as TYPOGRAPHY_VARIANTS, c as TypographyColor, d as TypographyRenderOptions, e as TypographyVariant, f as TypographyWeight } from '../types-CQyFuLqp.js';
|
||||
|
||||
declare const tachTypographyClassName: ({ variant, color, weight, clickable, className, }?: TypographyClassOptions) => string;
|
||||
declare const tachTypographyClassList: (options?: TypographyClassOptions) => string[];
|
||||
|
||||
type StyleRecord = Record<string, string | number>;
|
||||
declare const tachTypographyEllipsisStyle: (ellipsis?: EllipsisOptions) => StyleRecord | undefined;
|
||||
|
||||
export { EllipsisOptions, TypographyClassOptions, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle };
|
||||
81
packages/tach-typography/dist/core/index.js
vendored
Normal file
81
packages/tach-typography/dist/core/index.js
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
// src/core/types.ts
|
||||
var TYPOGRAPHY_VARIANTS = [
|
||||
"LargeTitle",
|
||||
"Title1",
|
||||
"Title2",
|
||||
"Title3",
|
||||
"Headline",
|
||||
"Body",
|
||||
"Inputs",
|
||||
"Subheadline",
|
||||
"FootnoteUnderline",
|
||||
"Footnote",
|
||||
"Caption",
|
||||
"Caption2",
|
||||
"AccentH1",
|
||||
"AccentH2",
|
||||
"AccentSubttl",
|
||||
"AccentSubttl2",
|
||||
"AccentCaption",
|
||||
"AccentCaption2",
|
||||
"AccentRegularM",
|
||||
"AccentRegularS",
|
||||
"AccentLargeTtl",
|
||||
"AppMediumBody",
|
||||
"AppMediumSubtext",
|
||||
"AppMediumSubtextUnderline"
|
||||
];
|
||||
var TYPOGRAPHY_COLORS = [
|
||||
"primary",
|
||||
"secondary",
|
||||
"tertiary",
|
||||
"quaternary",
|
||||
"link",
|
||||
"white",
|
||||
"dark",
|
||||
"alert",
|
||||
"malahit",
|
||||
"attantion"
|
||||
];
|
||||
|
||||
// src/core/classnames.ts
|
||||
var BASE_CLASS = "tach-typography";
|
||||
var join = (...parts) => parts.filter(Boolean).join(" ");
|
||||
var 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
|
||||
);
|
||||
};
|
||||
var tachTypographyClassList = (options = {}) => {
|
||||
return tachTypographyClassName(options).split(" ").filter(Boolean);
|
||||
};
|
||||
|
||||
// src/core/ellipsis.ts
|
||||
var tachTypographyEllipsisStyle = (ellipsis) => {
|
||||
if (!ellipsis) {
|
||||
return void 0;
|
||||
}
|
||||
const rows = typeof ellipsis === "object" ? ellipsis.rows ?? 1 : 1;
|
||||
return {
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
display: "-webkit-box",
|
||||
WebkitBoxOrient: "vertical",
|
||||
WebkitLineClamp: rows
|
||||
};
|
||||
};
|
||||
|
||||
export { TYPOGRAPHY_COLORS, TYPOGRAPHY_VARIANTS, tachTypographyClassList, tachTypographyClassName, tachTypographyEllipsisStyle };
|
||||
//# sourceMappingURL=index.js.map
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/tach-typography/dist/core/index.js.map
vendored
Normal file
1
packages/tach-typography/dist/core/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/core/types.ts","../../src/core/classnames.ts","../../src/core/ellipsis.ts"],"names":[],"mappings":";AAAO,IAAM,mBAAA,GAAsB;AAAA,EACjC,YAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,aAAA;AAAA,EACA,mBAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA,EACA,cAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA;AAAA,EACA,gBAAA;AAAA,EACA,gBAAA;AAAA,EACA,gBAAA;AAAA,EACA,gBAAA;AAAA,EACA,eAAA;AAAA,EACA,kBAAA;AAAA,EACA;AACF;AAEO,IAAM,iBAAA,GAAoB;AAAA,EAC/B,SAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,YAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF;;;ACpCA,IAAM,UAAA,GAAa,iBAAA;AAEnB,IAAM,IAAA,GAAO,IAAI,KAAA,KACf,KAAA,CAAM,OAAO,OAAO,CAAA,CAAE,KAAK,GAAG,CAAA;AAEzB,IAAM,0BAA0B,CAAC;AAAA,EACtC,OAAA,GAAU,MAAA;AAAA,EACV,KAAA,GAAQ,SAAA;AAAA,EACR,MAAA,GAAS,QAAA;AAAA,EACT,SAAA,GAAY,KAAA;AAAA,EACZ;AACF,CAAA,GAA4B,EAAC,KAAc;AACzC,EAAA,OAAO,IAAA;AAAA,IACL,UAAA;AAAA,IACA,CAAA,EAAG,UAAU,CAAA,EAAA,EAAK,OAAO,CAAA,CAAA;AAAA,IACzB,CAAA,EAAG,UAAU,CAAA,QAAA,EAAW,KAAK,CAAA,CAAA;AAAA,IAC7B,MAAA,KAAW,MAAA,IAAU,CAAA,EAAG,UAAU,CAAA,MAAA,CAAA;AAAA,IAClC,SAAA,IAAa,GAAG,UAAU,CAAA,SAAA,CAAA;AAAA,IAC1B;AAAA,GACF;AACF;AAEO,IAAM,uBAAA,GAA0B,CAAC,OAAA,GAAkC,EAAC,KAAgB;AACzF,EAAA,OAAO,wBAAwB,OAAO,CAAA,CACnC,MAAM,GAAG,CAAA,CACT,OAAO,OAAO,CAAA;AACnB;;;ACxBO,IAAM,2BAAA,GAA8B,CACzC,QAAA,KAC4B;AAC5B,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAAO,OAAO,QAAA,KAAa,QAAA,GAAW,QAAA,CAAS,QAAQ,CAAA,GAAI,CAAA;AAEjE,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,QAAA;AAAA,IACV,YAAA,EAAc,UAAA;AAAA,IACd,OAAA,EAAS,aAAA;AAAA,IACT,eAAA,EAAiB,UAAA;AAAA,IACjB,eAAA,EAAiB;AAAA,GACnB;AACF","file":"index.js","sourcesContent":["export const TYPOGRAPHY_VARIANTS = [\n \"LargeTitle\",\n \"Title1\",\n \"Title2\",\n \"Title3\",\n \"Headline\",\n \"Body\",\n \"Inputs\",\n \"Subheadline\",\n \"FootnoteUnderline\",\n \"Footnote\",\n \"Caption\",\n \"Caption2\",\n \"AccentH1\",\n \"AccentH2\",\n \"AccentSubttl\",\n \"AccentSubttl2\",\n \"AccentCaption\",\n \"AccentCaption2\",\n \"AccentRegularM\",\n \"AccentRegularS\",\n \"AccentLargeTtl\",\n \"AppMediumBody\",\n \"AppMediumSubtext\",\n \"AppMediumSubtextUnderline\",\n] as const;\n\nexport const TYPOGRAPHY_COLORS = [\n \"primary\",\n \"secondary\",\n \"tertiary\",\n \"quaternary\",\n \"link\",\n \"white\",\n \"dark\",\n \"alert\",\n \"malahit\",\n \"attantion\",\n] as const;\n\nexport type TypographyVariant = (typeof TYPOGRAPHY_VARIANTS)[number];\nexport type TypographyColor = (typeof TYPOGRAPHY_COLORS)[number];\nexport type TypographyWeight = \"normal\" | \"bold\";\n\nexport interface TypographyClassOptions {\n variant?: TypographyVariant;\n color?: TypographyColor;\n weight?: TypographyWeight;\n clickable?: boolean;\n className?: string | undefined;\n}\n\nexport type EllipsisOptions =\n | boolean\n | {\n rows?: number;\n };\n\nexport interface TypographyRenderOptions extends TypographyClassOptions {\n ellipsis?: EllipsisOptions;\n}\n","import type { TypographyClassOptions } from \"./types\";\n\nconst BASE_CLASS = \"tach-typography\";\n\nconst join = (...parts: Array<string | undefined | null | false>): string =>\n parts.filter(Boolean).join(\" \");\n\nexport const tachTypographyClassName = ({\n variant = \"Body\",\n color = \"primary\",\n weight = \"normal\",\n clickable = false,\n className,\n}: TypographyClassOptions = {}): string => {\n return join(\n BASE_CLASS,\n `${BASE_CLASS}--${variant}`,\n `${BASE_CLASS}--color-${color}`,\n weight === \"bold\" && `${BASE_CLASS}--bold`,\n clickable && `${BASE_CLASS}--pointer`,\n className,\n );\n};\n\nexport const tachTypographyClassList = (options: TypographyClassOptions = {}): string[] => {\n return tachTypographyClassName(options)\n .split(\" \")\n .filter(Boolean);\n};\n","import type { EllipsisOptions } from \"./types\";\n\ntype StyleRecord = Record<string, string | number>;\n\nexport const tachTypographyEllipsisStyle = (\n ellipsis?: EllipsisOptions,\n): StyleRecord | undefined => {\n if (!ellipsis) {\n return undefined;\n }\n\n const rows = typeof ellipsis === \"object\" ? ellipsis.rows ?? 1 : 1;\n\n return {\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n display: \"-webkit-box\",\n WebkitBoxOrient: \"vertical\",\n WebkitLineClamp: rows,\n };\n};\n"]}
|
||||
88
packages/tach-typography/dist/react/index.cjs
vendored
Normal file
88
packages/tach-typography/dist/react/index.cjs
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var antd = require('antd');
|
||||
var jsxRuntime = require('react/jsx-runtime');
|
||||
|
||||
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
||||
|
||||
var React__default = /*#__PURE__*/_interopDefault(React);
|
||||
|
||||
// src/react/index.tsx
|
||||
|
||||
// src/core/classnames.ts
|
||||
var BASE_CLASS = "tach-typography";
|
||||
var join = (...parts) => parts.filter(Boolean).join(" ");
|
||||
var 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
|
||||
);
|
||||
};
|
||||
var createTypographyVariant = (Component, variant) => {
|
||||
const Variant = React__default.default.forwardRef(
|
||||
({ color = "primary", weight = "normal", className, onClick, ...rest }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
||||
Component,
|
||||
{
|
||||
ref,
|
||||
className: tachTypographyClassName({
|
||||
variant,
|
||||
color,
|
||||
weight,
|
||||
className,
|
||||
clickable: Boolean(onClick)
|
||||
}),
|
||||
onClick,
|
||||
...rest
|
||||
}
|
||||
)
|
||||
);
|
||||
Variant.displayName = String(variant);
|
||||
return Variant;
|
||||
};
|
||||
var createTypographyComponent = (Component) => ({
|
||||
LargeTitle: createTypographyVariant(Component, "LargeTitle"),
|
||||
Title1: createTypographyVariant(Component, "Title1"),
|
||||
Title2: createTypographyVariant(Component, "Title2"),
|
||||
Title3: createTypographyVariant(Component, "Title3"),
|
||||
Headline: createTypographyVariant(Component, "Headline"),
|
||||
Body: createTypographyVariant(Component, "Body"),
|
||||
Inputs: createTypographyVariant(Component, "Inputs"),
|
||||
Subheadline: createTypographyVariant(Component, "Subheadline"),
|
||||
FootnoteUnderline: createTypographyVariant(Component, "FootnoteUnderline"),
|
||||
Footnote: createTypographyVariant(Component, "Footnote"),
|
||||
Caption: createTypographyVariant(Component, "Caption"),
|
||||
Caption2: createTypographyVariant(Component, "Caption2"),
|
||||
AccentH1: createTypographyVariant(Component, "AccentH1"),
|
||||
AccentH2: createTypographyVariant(Component, "AccentH2"),
|
||||
AccentSubttl: createTypographyVariant(Component, "AccentSubttl"),
|
||||
AccentSubttl2: createTypographyVariant(Component, "AccentSubttl2"),
|
||||
AccentCaption: createTypographyVariant(Component, "AccentCaption"),
|
||||
AccentCaption2: createTypographyVariant(Component, "AccentCaption2"),
|
||||
AccentRegularM: createTypographyVariant(Component, "AccentRegularM"),
|
||||
AccentRegularS: createTypographyVariant(Component, "AccentRegularS"),
|
||||
AccentLargeTtl: createTypographyVariant(Component, "AccentLargeTtl"),
|
||||
AppMediumBody: createTypographyVariant(Component, "AppMediumBody"),
|
||||
AppMediumSubtext: createTypographyVariant(Component, "AppMediumSubtext"),
|
||||
AppMediumSubtextUnderline: createTypographyVariant(Component, "AppMediumSubtextUnderline")
|
||||
});
|
||||
var TachTypography = {
|
||||
Text: createTypographyComponent(antd.Typography.Text),
|
||||
Paragraph: createTypographyComponent(antd.Typography.Paragraph),
|
||||
Link: createTypographyComponent(antd.Typography.Link),
|
||||
Title: createTypographyComponent(antd.Typography.Title)
|
||||
};
|
||||
|
||||
exports.TachTypography = TachTypography;
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
1
packages/tach-typography/dist/react/index.cjs.map
vendored
Normal file
1
packages/tach-typography/dist/react/index.cjs.map
vendored
Normal file
File diff suppressed because one or more lines are too long
121
packages/tach-typography/dist/react/index.d.cts
vendored
Normal file
121
packages/tach-typography/dist/react/index.d.cts
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
import React from 'react';
|
||||
import { LinkProps } from 'antd/lib/typography/Link';
|
||||
import { ParagraphProps } from 'antd/lib/typography/Paragraph';
|
||||
import { TextProps } from 'antd/lib/typography/Text';
|
||||
import { TitleProps } from 'antd/lib/typography/Title';
|
||||
import { c as TypographyColor, f as TypographyWeight } from '../types-CQyFuLqp.cjs';
|
||||
|
||||
interface AdditionalProps {
|
||||
color?: TypographyColor;
|
||||
weight?: TypographyWeight;
|
||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
className?: string | undefined;
|
||||
}
|
||||
declare const TachTypography: {
|
||||
Text: {
|
||||
LargeTitle: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title1: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title2: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title3: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Headline: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Body: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Inputs: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Subheadline: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
FootnoteUnderline: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Footnote: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption2: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH1: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH2: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl2: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption2: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularM: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularS: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentLargeTtl: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumBody: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtext: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtextUnderline: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
};
|
||||
Paragraph: {
|
||||
LargeTitle: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title1: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title2: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title3: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Headline: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Body: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Inputs: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Subheadline: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
FootnoteUnderline: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Footnote: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption2: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH1: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH2: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl2: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption2: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularM: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularS: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentLargeTtl: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumBody: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtext: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtextUnderline: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
};
|
||||
Link: {
|
||||
LargeTitle: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title1: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title2: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title3: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Headline: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Body: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Inputs: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Subheadline: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
FootnoteUnderline: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Footnote: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption2: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH1: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH2: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl2: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption2: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularM: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularS: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentLargeTtl: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumBody: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtext: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtextUnderline: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
};
|
||||
Title: {
|
||||
LargeTitle: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title1: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title2: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title3: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Headline: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Body: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Inputs: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Subheadline: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
FootnoteUnderline: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Footnote: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption2: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH1: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH2: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl2: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption2: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularM: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularS: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentLargeTtl: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumBody: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtext: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtextUnderline: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
};
|
||||
};
|
||||
|
||||
export { TachTypography };
|
||||
121
packages/tach-typography/dist/react/index.d.ts
vendored
Normal file
121
packages/tach-typography/dist/react/index.d.ts
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
import React from 'react';
|
||||
import { LinkProps } from 'antd/lib/typography/Link';
|
||||
import { ParagraphProps } from 'antd/lib/typography/Paragraph';
|
||||
import { TextProps } from 'antd/lib/typography/Text';
|
||||
import { TitleProps } from 'antd/lib/typography/Title';
|
||||
import { c as TypographyColor, f as TypographyWeight } from '../types-CQyFuLqp.js';
|
||||
|
||||
interface AdditionalProps {
|
||||
color?: TypographyColor;
|
||||
weight?: TypographyWeight;
|
||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
className?: string | undefined;
|
||||
}
|
||||
declare const TachTypography: {
|
||||
Text: {
|
||||
LargeTitle: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title1: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title2: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title3: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Headline: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Body: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Inputs: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Subheadline: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
FootnoteUnderline: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Footnote: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption2: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH1: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH2: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl2: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption2: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularM: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularS: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentLargeTtl: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumBody: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtext: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtextUnderline: React.ForwardRefExoticComponent<TextProps & Pick<ParagraphProps, "ellipsis"> & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
};
|
||||
Paragraph: {
|
||||
LargeTitle: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title1: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title2: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title3: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Headline: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Body: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Inputs: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Subheadline: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
FootnoteUnderline: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Footnote: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption2: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH1: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH2: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl2: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption2: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularM: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularS: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentLargeTtl: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumBody: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtext: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtextUnderline: React.ForwardRefExoticComponent<ParagraphProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
};
|
||||
Link: {
|
||||
LargeTitle: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title1: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title2: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title3: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Headline: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Body: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Inputs: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Subheadline: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
FootnoteUnderline: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Footnote: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption2: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH1: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH2: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl2: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption2: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularM: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularS: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentLargeTtl: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumBody: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtext: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtextUnderline: React.ForwardRefExoticComponent<LinkProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
};
|
||||
Title: {
|
||||
LargeTitle: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title1: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title2: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Title3: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Headline: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Body: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Inputs: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Subheadline: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
FootnoteUnderline: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Footnote: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
Caption2: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH1: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentH2: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentSubttl2: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentCaption2: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularM: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentRegularS: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AccentLargeTtl: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumBody: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtext: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
AppMediumSubtextUnderline: React.ForwardRefExoticComponent<TitleProps & AdditionalProps & React.RefAttributes<HTMLElement>>;
|
||||
};
|
||||
};
|
||||
|
||||
export { TachTypography };
|
||||
82
packages/tach-typography/dist/react/index.js
vendored
Normal file
82
packages/tach-typography/dist/react/index.js
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import { Typography } from 'antd';
|
||||
import { jsx } from 'react/jsx-runtime';
|
||||
|
||||
// src/react/index.tsx
|
||||
|
||||
// src/core/classnames.ts
|
||||
var BASE_CLASS = "tach-typography";
|
||||
var join = (...parts) => parts.filter(Boolean).join(" ");
|
||||
var 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
|
||||
);
|
||||
};
|
||||
var createTypographyVariant = (Component, variant) => {
|
||||
const Variant = React.forwardRef(
|
||||
({ color = "primary", weight = "normal", className, onClick, ...rest }, ref) => /* @__PURE__ */ jsx(
|
||||
Component,
|
||||
{
|
||||
ref,
|
||||
className: tachTypographyClassName({
|
||||
variant,
|
||||
color,
|
||||
weight,
|
||||
className,
|
||||
clickable: Boolean(onClick)
|
||||
}),
|
||||
onClick,
|
||||
...rest
|
||||
}
|
||||
)
|
||||
);
|
||||
Variant.displayName = String(variant);
|
||||
return Variant;
|
||||
};
|
||||
var createTypographyComponent = (Component) => ({
|
||||
LargeTitle: createTypographyVariant(Component, "LargeTitle"),
|
||||
Title1: createTypographyVariant(Component, "Title1"),
|
||||
Title2: createTypographyVariant(Component, "Title2"),
|
||||
Title3: createTypographyVariant(Component, "Title3"),
|
||||
Headline: createTypographyVariant(Component, "Headline"),
|
||||
Body: createTypographyVariant(Component, "Body"),
|
||||
Inputs: createTypographyVariant(Component, "Inputs"),
|
||||
Subheadline: createTypographyVariant(Component, "Subheadline"),
|
||||
FootnoteUnderline: createTypographyVariant(Component, "FootnoteUnderline"),
|
||||
Footnote: createTypographyVariant(Component, "Footnote"),
|
||||
Caption: createTypographyVariant(Component, "Caption"),
|
||||
Caption2: createTypographyVariant(Component, "Caption2"),
|
||||
AccentH1: createTypographyVariant(Component, "AccentH1"),
|
||||
AccentH2: createTypographyVariant(Component, "AccentH2"),
|
||||
AccentSubttl: createTypographyVariant(Component, "AccentSubttl"),
|
||||
AccentSubttl2: createTypographyVariant(Component, "AccentSubttl2"),
|
||||
AccentCaption: createTypographyVariant(Component, "AccentCaption"),
|
||||
AccentCaption2: createTypographyVariant(Component, "AccentCaption2"),
|
||||
AccentRegularM: createTypographyVariant(Component, "AccentRegularM"),
|
||||
AccentRegularS: createTypographyVariant(Component, "AccentRegularS"),
|
||||
AccentLargeTtl: createTypographyVariant(Component, "AccentLargeTtl"),
|
||||
AppMediumBody: createTypographyVariant(Component, "AppMediumBody"),
|
||||
AppMediumSubtext: createTypographyVariant(Component, "AppMediumSubtext"),
|
||||
AppMediumSubtextUnderline: createTypographyVariant(Component, "AppMediumSubtextUnderline")
|
||||
});
|
||||
var TachTypography = {
|
||||
Text: createTypographyComponent(Typography.Text),
|
||||
Paragraph: createTypographyComponent(Typography.Paragraph),
|
||||
Link: createTypographyComponent(Typography.Link),
|
||||
Title: createTypographyComponent(Typography.Title)
|
||||
};
|
||||
|
||||
export { TachTypography };
|
||||
//# sourceMappingURL=index.js.map
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/tach-typography/dist/react/index.js.map
vendored
Normal file
1
packages/tach-typography/dist/react/index.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
103
packages/tach-typography/dist/styles.css
vendored
Normal file
103
packages/tach-typography/dist/styles.css
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
.tach-typography {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ant-typography.tach-typography {
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.tach-typography--pointer,
|
||||
.ant-typography.tach-typography--pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tach-typography--bold,
|
||||
.ant-typography.tach-typography--bold {
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.tach-typography--color-primary,
|
||||
.ant-typography.tach-typography--color-primary { color: var(--Text-Primary); }
|
||||
.tach-typography--color-secondary,
|
||||
.ant-typography.tach-typography--color-secondary { color: var(--Text-Secondary); }
|
||||
.tach-typography--color-tertiary,
|
||||
.ant-typography.tach-typography--color-tertiary { color: var(--Text-Tertiary); }
|
||||
.tach-typography--color-quaternary,
|
||||
.ant-typography.tach-typography--color-quaternary { color: var(--Text-Quaternary); }
|
||||
.tach-typography--color-link,
|
||||
.ant-typography.tach-typography--color-link { color: var(--System-HashtagsInPost); }
|
||||
.tach-typography--color-white,
|
||||
.ant-typography.tach-typography--color-white { color: var(--Default-White); }
|
||||
.tach-typography--color-dark,
|
||||
.ant-typography.tach-typography--color-dark { color: var(--Default-Dark); }
|
||||
.tach-typography--color-alert,
|
||||
.ant-typography.tach-typography--color-alert { color: var(--System-Alert); }
|
||||
.tach-typography--color-malahit,
|
||||
.ant-typography.tach-typography--color-malahit { color: var(--Accent-Malahit); }
|
||||
.tach-typography--color-attantion,
|
||||
.ant-typography.tach-typography--color-attantion { color: var(--System-Attantion); }
|
||||
|
||||
.tach-typography--LargeTitle,
|
||||
.ant-typography.tach-typography--LargeTitle { font-family: Inter, sans-serif; font-size: 38px; font-weight: 500; line-height: 46px; }
|
||||
.tach-typography--Title1,
|
||||
.ant-typography.tach-typography--Title1 { font-family: Inter, sans-serif; font-size: 28px; font-weight: 500; line-height: 34px; }
|
||||
.tach-typography--Title2,
|
||||
.ant-typography.tach-typography--Title2 { font-family: Inter, sans-serif; font-size: 22px; font-weight: 500; line-height: 28px; }
|
||||
.tach-typography--Title3,
|
||||
.ant-typography.tach-typography--Title3 { font-family: Inter, sans-serif; font-size: 20px; font-weight: 500; line-height: 26px; }
|
||||
.tach-typography--Headline,
|
||||
.ant-typography.tach-typography--Headline { font-family: Inter, sans-serif; font-size: 16px; font-weight: 500; line-height: 24px; }
|
||||
.tach-typography--Body,
|
||||
.tach-typography--AppMediumBody,
|
||||
.ant-typography.tach-typography--Body,
|
||||
.ant-typography.tach-typography--AppMediumBody { font-family: Inter, sans-serif; font-size: 14px; font-weight: 500; line-height: 20px; }
|
||||
.tach-typography--Inputs,
|
||||
.ant-typography.tach-typography--Inputs { font-family: Inter, sans-serif; font-size: 14px; font-weight: 500; line-height: 24px; }
|
||||
.tach-typography--Subheadline,
|
||||
.ant-typography.tach-typography--Subheadline { font-family: Inter, sans-serif; font-size: 14px; font-weight: 500; line-height: 18px; }
|
||||
.tach-typography--FootnoteUnderline,
|
||||
.ant-typography.tach-typography--FootnoteUnderline { font-family: Inter, sans-serif; font-size: 13px; font-weight: 500; line-height: 18px; text-decoration: underline; }
|
||||
.tach-typography--Footnote,
|
||||
.ant-typography.tach-typography--Footnote { font-family: Inter, sans-serif; font-size: 13px; font-weight: 500; line-height: 18px; }
|
||||
.tach-typography--Caption,
|
||||
.ant-typography.tach-typography--Caption { font-family: Inter, sans-serif; font-size: 10px; font-weight: 500; line-height: 12px; text-transform: uppercase; }
|
||||
.tach-typography--Caption2,
|
||||
.ant-typography.tach-typography--Caption2 { font-family: Inter, sans-serif; font-size: 8px; font-weight: 500; line-height: 10px; text-transform: uppercase; }
|
||||
|
||||
.tach-typography--AccentH1,
|
||||
.ant-typography.tach-typography--AccentH1 { font-family: Unbounded, sans-serif; font-size: 20px; font-weight: 700; line-height: 30px; }
|
||||
.tach-typography--AccentH2,
|
||||
.ant-typography.tach-typography--AccentH2 { font-family: Unbounded, sans-serif; font-size: 16px; font-weight: 700; line-height: 24px; }
|
||||
.tach-typography--AccentSubttl,
|
||||
.ant-typography.tach-typography--AccentSubttl { font-family: Unbounded, sans-serif; font-size: 14px; font-weight: 700; line-height: 22px; }
|
||||
.tach-typography--AccentSubttl2,
|
||||
.ant-typography.tach-typography--AccentSubttl2 { font-family: Unbounded, sans-serif; font-size: 12px; font-weight: 700; line-height: 20px; }
|
||||
.tach-typography--AccentCaption,
|
||||
.ant-typography.tach-typography--AccentCaption { font-family: Unbounded, sans-serif; font-size: 9px; font-weight: 700; line-height: 12px; text-transform: uppercase; }
|
||||
.tach-typography--AccentCaption2,
|
||||
.ant-typography.tach-typography--AccentCaption2 { font-family: Unbounded, sans-serif; font-size: 7px; font-weight: 700; line-height: 10px; text-transform: uppercase; }
|
||||
.tach-typography--AccentRegularM,
|
||||
.ant-typography.tach-typography--AccentRegularM { font-family: Unbounded, sans-serif; font-size: 14px; font-weight: 400; line-height: 22px; }
|
||||
.tach-typography--AccentRegularS,
|
||||
.ant-typography.tach-typography--AccentRegularS { font-family: Unbounded, sans-serif; font-size: 12px; font-weight: 400; line-height: 20px; }
|
||||
.tach-typography--AccentLargeTtl,
|
||||
.ant-typography.tach-typography--AccentLargeTtl { font-family: Unbounded, sans-serif; font-size: 38px; font-weight: 700; line-height: 52px; }
|
||||
.tach-typography--AppMediumSubtext,
|
||||
.ant-typography.tach-typography--AppMediumSubtext { text-align: center; font-family: Inter, sans-serif; font-size: 11px; font-weight: 400; line-height: 17px; }
|
||||
.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; }
|
||||
|
||||
@media (max-width: 575px) {
|
||||
.tach-typography--AccentLargeTtl,
|
||||
.ant-typography.tach-typography--AccentLargeTtl {
|
||||
font-size: 20px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.tach-typography--AccentRegularM,
|
||||
.ant-typography.tach-typography--AccentRegularM {
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
20
packages/tach-typography/dist/types-CQyFuLqp.d.cts
vendored
Normal file
20
packages/tach-typography/dist/types-CQyFuLqp.d.cts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
declare const TYPOGRAPHY_VARIANTS: readonly ["LargeTitle", "Title1", "Title2", "Title3", "Headline", "Body", "Inputs", "Subheadline", "FootnoteUnderline", "Footnote", "Caption", "Caption2", "AccentH1", "AccentH2", "AccentSubttl", "AccentSubttl2", "AccentCaption", "AccentCaption2", "AccentRegularM", "AccentRegularS", "AccentLargeTtl", "AppMediumBody", "AppMediumSubtext", "AppMediumSubtextUnderline"];
|
||||
declare const TYPOGRAPHY_COLORS: readonly ["primary", "secondary", "tertiary", "quaternary", "link", "white", "dark", "alert", "malahit", "attantion"];
|
||||
type TypographyVariant = (typeof TYPOGRAPHY_VARIANTS)[number];
|
||||
type TypographyColor = (typeof TYPOGRAPHY_COLORS)[number];
|
||||
type TypographyWeight = "normal" | "bold";
|
||||
interface TypographyClassOptions {
|
||||
variant?: TypographyVariant;
|
||||
color?: TypographyColor;
|
||||
weight?: TypographyWeight;
|
||||
clickable?: boolean;
|
||||
className?: string | undefined;
|
||||
}
|
||||
type EllipsisOptions = boolean | {
|
||||
rows?: number;
|
||||
};
|
||||
interface TypographyRenderOptions extends TypographyClassOptions {
|
||||
ellipsis?: EllipsisOptions;
|
||||
}
|
||||
|
||||
export { type EllipsisOptions as E, type TypographyClassOptions as T, TYPOGRAPHY_COLORS as a, TYPOGRAPHY_VARIANTS as b, type TypographyColor as c, type TypographyRenderOptions as d, type TypographyVariant as e, type TypographyWeight as f };
|
||||
20
packages/tach-typography/dist/types-CQyFuLqp.d.ts
vendored
Normal file
20
packages/tach-typography/dist/types-CQyFuLqp.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
declare const TYPOGRAPHY_VARIANTS: readonly ["LargeTitle", "Title1", "Title2", "Title3", "Headline", "Body", "Inputs", "Subheadline", "FootnoteUnderline", "Footnote", "Caption", "Caption2", "AccentH1", "AccentH2", "AccentSubttl", "AccentSubttl2", "AccentCaption", "AccentCaption2", "AccentRegularM", "AccentRegularS", "AccentLargeTtl", "AppMediumBody", "AppMediumSubtext", "AppMediumSubtextUnderline"];
|
||||
declare const TYPOGRAPHY_COLORS: readonly ["primary", "secondary", "tertiary", "quaternary", "link", "white", "dark", "alert", "malahit", "attantion"];
|
||||
type TypographyVariant = (typeof TYPOGRAPHY_VARIANTS)[number];
|
||||
type TypographyColor = (typeof TYPOGRAPHY_COLORS)[number];
|
||||
type TypographyWeight = "normal" | "bold";
|
||||
interface TypographyClassOptions {
|
||||
variant?: TypographyVariant;
|
||||
color?: TypographyColor;
|
||||
weight?: TypographyWeight;
|
||||
clickable?: boolean;
|
||||
className?: string | undefined;
|
||||
}
|
||||
type EllipsisOptions = boolean | {
|
||||
rows?: number;
|
||||
};
|
||||
interface TypographyRenderOptions extends TypographyClassOptions {
|
||||
ellipsis?: EllipsisOptions;
|
||||
}
|
||||
|
||||
export { type EllipsisOptions as E, type TypographyClassOptions as T, TYPOGRAPHY_COLORS as a, TYPOGRAPHY_VARIANTS as b, type TypographyColor as c, type TypographyRenderOptions as d, type TypographyVariant as e, type TypographyWeight as f };
|
||||
136
packages/tach-typography/package.json
Normal file
136
packages/tach-typography/package.json
Normal file
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"name": "@hublib-web/tach-typography",
|
||||
"version": "0.1.0",
|
||||
"description": "Cross-framework typography package for React and Angular",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"main": "./dist/core/index.cjs",
|
||||
"module": "./dist/core/index.js",
|
||||
"types": "./dist/core/index.d.ts",
|
||||
"sideEffects": [
|
||||
"./dist/styles.css"
|
||||
],
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md",
|
||||
"styles"
|
||||
],
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"react": [
|
||||
"dist/react/index.d.ts"
|
||||
],
|
||||
"angular": [
|
||||
"dist/angular/index.d.ts"
|
||||
],
|
||||
"core": [
|
||||
"dist/core/index.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/core/index.d.ts",
|
||||
"import": "./dist/core/index.js",
|
||||
"require": "./dist/core/index.cjs"
|
||||
},
|
||||
"./core": {
|
||||
"types": "./dist/core/index.d.ts",
|
||||
"import": "./dist/core/index.js",
|
||||
"require": "./dist/core/index.cjs"
|
||||
},
|
||||
"./react": {
|
||||
"types": "./dist/react/index.d.ts",
|
||||
"import": "./dist/react/index.js",
|
||||
"require": "./dist/react/index.cjs"
|
||||
},
|
||||
"./angular": {
|
||||
"types": "./dist/angular/index.d.ts",
|
||||
"import": "./dist/angular/index.js",
|
||||
"require": "./dist/angular/index.cjs"
|
||||
},
|
||||
"./styles.css": "./dist/styles.css",
|
||||
"./styles/typography-vars.scss": "./styles/typography-vars.scss"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn clean && tsup && node ./scripts/copy-styles.mjs",
|
||||
"clean": "rm -rf dist",
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"test": "vitest run",
|
||||
"lint": "eslint src --ext .ts,.tsx",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"storybook:build": "storybook build"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/animations": ">=17.0.0",
|
||||
"@angular/common": ">=17.0.0",
|
||||
"@angular/core": ">=17.0.0",
|
||||
"@angular/forms": ">=17.0.0",
|
||||
"@angular/platform-browser": ">=17.0.0",
|
||||
"@angular/router": ">=17.0.0",
|
||||
"antd": ">=5.0.0",
|
||||
"ng-zorro-antd": ">=17.0.0",
|
||||
"react": ">=18.0.0",
|
||||
"react-dom": ">=18.0.0",
|
||||
"rxjs": ">=7.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@angular/animations": {
|
||||
"optional": true
|
||||
},
|
||||
"@angular/common": {
|
||||
"optional": true
|
||||
},
|
||||
"@angular/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@angular/forms": {
|
||||
"optional": true
|
||||
},
|
||||
"@angular/platform-browser": {
|
||||
"optional": true
|
||||
},
|
||||
"@angular/router": {
|
||||
"optional": true
|
||||
},
|
||||
"antd": {
|
||||
"optional": true
|
||||
},
|
||||
"ng-zorro-antd": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"rxjs": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/animations": "^20.3.17",
|
||||
"@angular/common": "^20.3.17",
|
||||
"@angular/core": "^20.3.17",
|
||||
"@angular/forms": "^20.3.17",
|
||||
"@angular/platform-browser": "^20.3.17",
|
||||
"@angular/router": "^20.3.17",
|
||||
"@storybook/addon-a11y": "8.6.14",
|
||||
"@storybook/addon-essentials": "8.6.14",
|
||||
"@storybook/addon-interactions": "8.6.14",
|
||||
"@storybook/blocks": "8.6.14",
|
||||
"@storybook/react-vite": "8.6.14",
|
||||
"@storybook/test": "8.6.14",
|
||||
"@types/react": "^19.2.2",
|
||||
"antd": "^5.29.3",
|
||||
"ng-zorro-antd": "^20.4.4",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"rxjs": "^7.8.2",
|
||||
"storybook": "8.6.14",
|
||||
"tsup": "^8.5.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "6.4.1"
|
||||
}
|
||||
}
|
||||
12
packages/tach-typography/scripts/copy-styles.mjs
Normal file
12
packages/tach-typography/scripts/copy-styles.mjs
Normal file
@@ -0,0 +1,12 @@
|
||||
import { cpSync, existsSync, mkdirSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
|
||||
const source = resolve("src/styles/tach-typography.css");
|
||||
const destination = resolve("dist/styles.css");
|
||||
|
||||
if (!existsSync(source)) {
|
||||
throw new Error(`Styles file not found: ${source}`);
|
||||
}
|
||||
|
||||
mkdirSync(dirname(destination), { recursive: true });
|
||||
cpSync(source, destination);
|
||||
140
packages/tach-typography/src/angular/index.ts
Normal file
140
packages/tach-typography/src/angular/index.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { Directive, ElementRef, Input, OnChanges, Renderer2, SimpleChanges } from "@angular/core";
|
||||
import { NzTypographyModule } from "ng-zorro-antd/typography";
|
||||
|
||||
import {
|
||||
tachTypographyClassList,
|
||||
tachTypographyClassName,
|
||||
tachTypographyEllipsisStyle,
|
||||
type EllipsisOptions,
|
||||
type TypographyClassOptions,
|
||||
type TypographyColor,
|
||||
type TypographyRenderOptions,
|
||||
type TypographyVariant,
|
||||
type TypographyWeight,
|
||||
} from "../core";
|
||||
|
||||
export type AngularTypographyClassInput = TypographyClassOptions;
|
||||
|
||||
export interface AngularTypographyRenderOptions extends TypographyRenderOptions {
|
||||
preserveStyle?: Record<string, string | number>;
|
||||
}
|
||||
|
||||
const camelToKebab = (value: string): string =>
|
||||
value.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
|
||||
|
||||
const toCssProperty = (styleKey: string): string => {
|
||||
if (styleKey.startsWith("Webkit")) {
|
||||
return `-webkit-${camelToKebab(styleKey.slice(6))}`;
|
||||
}
|
||||
|
||||
return camelToKebab(styleKey);
|
||||
};
|
||||
|
||||
export const tachAngularTypographyClassName = (
|
||||
options: AngularTypographyClassInput = {},
|
||||
): string => {
|
||||
return tachTypographyClassName(options);
|
||||
};
|
||||
|
||||
export const tachAngularTypographyClassList = (
|
||||
options: AngularTypographyClassInput = {},
|
||||
): string[] => {
|
||||
return tachTypographyClassList(options);
|
||||
};
|
||||
|
||||
export const tachAngularTypographyStyles = (
|
||||
ellipsis?: EllipsisOptions,
|
||||
preserveStyle: Record<string, string | number> = {},
|
||||
): Record<string, string | number> => {
|
||||
const ellipsisStyle = tachTypographyEllipsisStyle(ellipsis);
|
||||
|
||||
if (!ellipsisStyle) {
|
||||
return preserveStyle;
|
||||
}
|
||||
|
||||
return {
|
||||
...ellipsisStyle,
|
||||
...preserveStyle,
|
||||
};
|
||||
};
|
||||
|
||||
@Directive({
|
||||
selector: "[tachTypography]",
|
||||
standalone: true,
|
||||
})
|
||||
export class TachTypographyDirective implements OnChanges {
|
||||
@Input() tachTypography: TypographyVariant | "" | undefined;
|
||||
@Input() tachTypographyVariant: TypographyVariant = "Body";
|
||||
@Input() tachTypographyColor: TypographyColor = "primary";
|
||||
@Input() tachTypographyWeight: TypographyWeight = "normal";
|
||||
@Input() tachTypographyClickable = false;
|
||||
@Input() tachTypographyClassName: string | undefined;
|
||||
@Input() tachTypographyEllipsis: EllipsisOptions | undefined;
|
||||
|
||||
private readonly appliedClasses = new Set<string>();
|
||||
private readonly appliedStyleProperties = new Set<string>();
|
||||
|
||||
constructor(
|
||||
private readonly elementRef: ElementRef<HTMLElement>,
|
||||
private readonly renderer: Renderer2,
|
||||
) {}
|
||||
|
||||
ngOnChanges(_changes: SimpleChanges): void {
|
||||
this.syncClasses();
|
||||
this.syncEllipsisStyles();
|
||||
}
|
||||
|
||||
private syncClasses(): void {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private syncEllipsisStyles(): void {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [NzTypographyModule, TachTypographyDirective],
|
||||
exports: [NzTypographyModule, TachTypographyDirective],
|
||||
})
|
||||
export class TachTypographyNzModule {}
|
||||
29
packages/tach-typography/src/core/classnames.ts
Normal file
29
packages/tach-typography/src/core/classnames.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { TypographyClassOptions } from "./types";
|
||||
|
||||
const BASE_CLASS = "tach-typography";
|
||||
|
||||
const join = (...parts: Array<string | undefined | null | false>): string =>
|
||||
parts.filter(Boolean).join(" ");
|
||||
|
||||
export const tachTypographyClassName = ({
|
||||
variant = "Body",
|
||||
color = "primary",
|
||||
weight = "normal",
|
||||
clickable = false,
|
||||
className,
|
||||
}: TypographyClassOptions = {}): string => {
|
||||
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: TypographyClassOptions = {}): string[] => {
|
||||
return tachTypographyClassName(options)
|
||||
.split(" ")
|
||||
.filter(Boolean);
|
||||
};
|
||||
21
packages/tach-typography/src/core/ellipsis.ts
Normal file
21
packages/tach-typography/src/core/ellipsis.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { EllipsisOptions } from "./types";
|
||||
|
||||
type StyleRecord = Record<string, string | number>;
|
||||
|
||||
export const tachTypographyEllipsisStyle = (
|
||||
ellipsis?: EllipsisOptions,
|
||||
): StyleRecord | undefined => {
|
||||
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,
|
||||
};
|
||||
};
|
||||
56
packages/tach-typography/src/core/index.test.ts
Normal file
56
packages/tach-typography/src/core/index.test.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
tachTypographyClassList,
|
||||
tachTypographyClassName,
|
||||
tachTypographyEllipsisStyle,
|
||||
} from "./index";
|
||||
|
||||
describe("tachTypographyClassName", () => {
|
||||
it("builds className string with defaults", () => {
|
||||
expect(tachTypographyClassName()).toBe(
|
||||
"tach-typography tach-typography--Body tach-typography--color-primary",
|
||||
);
|
||||
});
|
||||
|
||||
it("adds optional states", () => {
|
||||
expect(
|
||||
tachTypographyClassName({
|
||||
variant: "AccentH1",
|
||||
color: "link",
|
||||
weight: "bold",
|
||||
clickable: true,
|
||||
className: "custom",
|
||||
}),
|
||||
).toBe(
|
||||
"tach-typography tach-typography--AccentH1 tach-typography--color-link tach-typography--bold tach-typography--pointer custom",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns list helper", () => {
|
||||
expect(tachTypographyClassList({ variant: "Title1" })).toEqual([
|
||||
"tach-typography",
|
||||
"tach-typography--Title1",
|
||||
"tach-typography--color-primary",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tachTypographyEllipsisStyle", () => {
|
||||
it("returns undefined without ellipsis", () => {
|
||||
expect(tachTypographyEllipsisStyle()).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns one-line style for true", () => {
|
||||
expect(tachTypographyEllipsisStyle(true)).toMatchObject({
|
||||
WebkitLineClamp: 1,
|
||||
overflow: "hidden",
|
||||
});
|
||||
});
|
||||
|
||||
it("respects rows value", () => {
|
||||
expect(tachTypographyEllipsisStyle({ rows: 3 })).toMatchObject({
|
||||
WebkitLineClamp: 3,
|
||||
});
|
||||
});
|
||||
});
|
||||
3
packages/tach-typography/src/core/index.ts
Normal file
3
packages/tach-typography/src/core/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./types";
|
||||
export * from "./classnames";
|
||||
export * from "./ellipsis";
|
||||
61
packages/tach-typography/src/core/types.ts
Normal file
61
packages/tach-typography/src/core/types.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
export const TYPOGRAPHY_VARIANTS = [
|
||||
"LargeTitle",
|
||||
"Title1",
|
||||
"Title2",
|
||||
"Title3",
|
||||
"Headline",
|
||||
"Body",
|
||||
"Inputs",
|
||||
"Subheadline",
|
||||
"FootnoteUnderline",
|
||||
"Footnote",
|
||||
"Caption",
|
||||
"Caption2",
|
||||
"AccentH1",
|
||||
"AccentH2",
|
||||
"AccentSubttl",
|
||||
"AccentSubttl2",
|
||||
"AccentCaption",
|
||||
"AccentCaption2",
|
||||
"AccentRegularM",
|
||||
"AccentRegularS",
|
||||
"AccentLargeTtl",
|
||||
"AppMediumBody",
|
||||
"AppMediumSubtext",
|
||||
"AppMediumSubtextUnderline",
|
||||
] as const;
|
||||
|
||||
export const TYPOGRAPHY_COLORS = [
|
||||
"primary",
|
||||
"secondary",
|
||||
"tertiary",
|
||||
"quaternary",
|
||||
"link",
|
||||
"white",
|
||||
"dark",
|
||||
"alert",
|
||||
"malahit",
|
||||
"attantion",
|
||||
] as const;
|
||||
|
||||
export type TypographyVariant = (typeof TYPOGRAPHY_VARIANTS)[number];
|
||||
export type TypographyColor = (typeof TYPOGRAPHY_COLORS)[number];
|
||||
export type TypographyWeight = "normal" | "bold";
|
||||
|
||||
export interface TypographyClassOptions {
|
||||
variant?: TypographyVariant;
|
||||
color?: TypographyColor;
|
||||
weight?: TypographyWeight;
|
||||
clickable?: boolean;
|
||||
className?: string | undefined;
|
||||
}
|
||||
|
||||
export type EllipsisOptions =
|
||||
| boolean
|
||||
| {
|
||||
rows?: number;
|
||||
};
|
||||
|
||||
export interface TypographyRenderOptions extends TypographyClassOptions {
|
||||
ellipsis?: EllipsisOptions;
|
||||
}
|
||||
82
packages/tach-typography/src/react/index.tsx
Normal file
82
packages/tach-typography/src/react/index.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import React from "react";
|
||||
|
||||
import { Typography } from "antd";
|
||||
import type { LinkProps } from "antd/lib/typography/Link";
|
||||
import type { ParagraphProps } from "antd/lib/typography/Paragraph";
|
||||
import type { TextProps } from "antd/lib/typography/Text";
|
||||
import type { TitleProps } from "antd/lib/typography/Title";
|
||||
|
||||
import {
|
||||
tachTypographyClassName,
|
||||
type TypographyColor,
|
||||
type TypographyVariant,
|
||||
type TypographyWeight,
|
||||
} from "../core";
|
||||
|
||||
interface AdditionalProps {
|
||||
color?: TypographyColor;
|
||||
weight?: TypographyWeight;
|
||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
className?: string | undefined;
|
||||
}
|
||||
|
||||
const createTypographyVariant = <P extends object>(
|
||||
Component: React.ComponentType<P>,
|
||||
variant: TypographyVariant,
|
||||
) => {
|
||||
const Variant = React.forwardRef<HTMLElement, P & AdditionalProps>(
|
||||
({ color = "primary", weight = "normal", className, onClick, ...rest }, ref) => (
|
||||
<Component
|
||||
ref={ref as never}
|
||||
className={tachTypographyClassName({
|
||||
variant,
|
||||
color,
|
||||
weight,
|
||||
className,
|
||||
clickable: Boolean(onClick),
|
||||
})}
|
||||
onClick={onClick}
|
||||
{...(rest as P)}
|
||||
/>
|
||||
),
|
||||
);
|
||||
|
||||
Variant.displayName = String(variant);
|
||||
|
||||
return Variant;
|
||||
};
|
||||
|
||||
const createTypographyComponent = <P extends object>(Component: React.ComponentType<P>) => ({
|
||||
LargeTitle: createTypographyVariant(Component, "LargeTitle"),
|
||||
Title1: createTypographyVariant(Component, "Title1"),
|
||||
Title2: createTypographyVariant(Component, "Title2"),
|
||||
Title3: createTypographyVariant(Component, "Title3"),
|
||||
Headline: createTypographyVariant(Component, "Headline"),
|
||||
Body: createTypographyVariant(Component, "Body"),
|
||||
Inputs: createTypographyVariant(Component, "Inputs"),
|
||||
Subheadline: createTypographyVariant(Component, "Subheadline"),
|
||||
FootnoteUnderline: createTypographyVariant(Component, "FootnoteUnderline"),
|
||||
Footnote: createTypographyVariant(Component, "Footnote"),
|
||||
Caption: createTypographyVariant(Component, "Caption"),
|
||||
Caption2: createTypographyVariant(Component, "Caption2"),
|
||||
|
||||
AccentH1: createTypographyVariant(Component, "AccentH1"),
|
||||
AccentH2: createTypographyVariant(Component, "AccentH2"),
|
||||
AccentSubttl: createTypographyVariant(Component, "AccentSubttl"),
|
||||
AccentSubttl2: createTypographyVariant(Component, "AccentSubttl2"),
|
||||
AccentCaption: createTypographyVariant(Component, "AccentCaption"),
|
||||
AccentCaption2: createTypographyVariant(Component, "AccentCaption2"),
|
||||
AccentRegularM: createTypographyVariant(Component, "AccentRegularM"),
|
||||
AccentRegularS: createTypographyVariant(Component, "AccentRegularS"),
|
||||
AccentLargeTtl: createTypographyVariant(Component, "AccentLargeTtl"),
|
||||
AppMediumBody: createTypographyVariant(Component, "AppMediumBody"),
|
||||
AppMediumSubtext: createTypographyVariant(Component, "AppMediumSubtext"),
|
||||
AppMediumSubtextUnderline: createTypographyVariant(Component, "AppMediumSubtextUnderline"),
|
||||
});
|
||||
|
||||
export const TachTypography = {
|
||||
Text: createTypographyComponent<TextProps & Pick<ParagraphProps, "ellipsis">>(Typography.Text),
|
||||
Paragraph: createTypographyComponent<ParagraphProps>(Typography.Paragraph),
|
||||
Link: createTypographyComponent<LinkProps>(Typography.Link),
|
||||
Title: createTypographyComponent<TitleProps>(Typography.Title),
|
||||
};
|
||||
@@ -0,0 +1,183 @@
|
||||
import React from "react";
|
||||
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { expect, fn, userEvent, within } from "@storybook/test";
|
||||
|
||||
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;
|
||||
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 PlaygroundArgs {
|
||||
namespace: TypographyNamespace;
|
||||
variant: TypographyVariant;
|
||||
color: TypographyColor;
|
||||
weight: TypographyWeight;
|
||||
children: string;
|
||||
clickable: boolean;
|
||||
ellipsisRows: number;
|
||||
href: string;
|
||||
titleLevel: 1 | 2 | 3 | 4 | 5;
|
||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
}
|
||||
|
||||
const renderTypography = (args: PlaygroundArgs) => {
|
||||
const Component = getVariantComponent(args.namespace, args.variant);
|
||||
|
||||
const componentProps: VariantComponentProps = {
|
||||
color: args.color,
|
||||
weight: args.weight,
|
||||
};
|
||||
|
||||
if (args.clickable && args.onClick) {
|
||||
componentProps.onClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
args.onClick?.(event);
|
||||
};
|
||||
}
|
||||
|
||||
if (args.namespace === "Text" || args.namespace === "Paragraph") {
|
||||
componentProps.ellipsis = args.ellipsisRows > 0 ? { rows: args.ellipsisRows } : false;
|
||||
}
|
||||
|
||||
if (args.namespace === "Link") {
|
||||
componentProps.href = args.href;
|
||||
}
|
||||
|
||||
if (args.namespace === "Title") {
|
||||
componentProps.level = args.titleLevel;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="tach-story-surface">
|
||||
<Component {...componentProps}>{args.children}</Component>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const meta: Meta<PlaygroundArgs> = {
|
||||
title: "TachTypography/Playground",
|
||||
tags: ["autodocs"],
|
||||
render: renderTypography,
|
||||
args: {
|
||||
namespace: "Text",
|
||||
variant: "Body",
|
||||
color: "primary",
|
||||
weight: "normal",
|
||||
children: "TachTypography playground text",
|
||||
clickable: false,
|
||||
ellipsisRows: 0,
|
||||
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"],
|
||||
},
|
||||
children: {
|
||||
control: "text",
|
||||
},
|
||||
clickable: {
|
||||
control: "boolean",
|
||||
},
|
||||
ellipsisRows: {
|
||||
control: {
|
||||
type: "number",
|
||||
min: 0,
|
||||
max: 5,
|
||||
},
|
||||
description: "Works for Text and Paragraph namespaces",
|
||||
},
|
||||
href: {
|
||||
control: "text",
|
||||
description: "Works for Link namespace",
|
||||
},
|
||||
titleLevel: {
|
||||
control: "inline-radio",
|
||||
options: [1, 2, 3, 4, 5],
|
||||
description: "Works for Title namespace",
|
||||
},
|
||||
onClick: {
|
||||
table: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"Interactive playground for all TachTypography namespaces with full token and prop controls.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Interactive: Story = {};
|
||||
|
||||
export const WithEllipsis: Story = {
|
||||
args: {
|
||||
namespace: "Paragraph",
|
||||
variant: "Body",
|
||||
ellipsisRows: 2,
|
||||
children:
|
||||
"This paragraph demonstrates multi-line truncation in Storybook. Increase or decrease ellipsisRows to validate visual behavior and clipping boundaries.",
|
||||
},
|
||||
};
|
||||
|
||||
export const ClickInteraction: Story = {
|
||||
args: {
|
||||
namespace: "Text",
|
||||
variant: "Subheadline",
|
||||
clickable: true,
|
||||
children: "Click this text to run interaction assertion",
|
||||
onClick: fn(),
|
||||
},
|
||||
play: async ({ canvasElement, args }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(canvas.getByText(args.children));
|
||||
await expect(args.onClick as ReturnType<typeof fn>).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,105 @@
|
||||
import React from "react";
|
||||
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
const meta = {
|
||||
title: "TachTypography/Reference",
|
||||
tags: ["autodocs"],
|
||||
} satisfies Meta;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const PropsMatrix: Story = {
|
||||
render: () => (
|
||||
<div className="tach-story-surface tach-story-stack">
|
||||
<table className="tach-story-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Prop</th>
|
||||
<th>Type</th>
|
||||
<th>React</th>
|
||||
<th>Angular</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>variant</td>
|
||||
<td>TypographyVariant</td>
|
||||
<td>TachTypography.[Text|Paragraph|Link|Title].Variant</td>
|
||||
<td>tachTypography / tachTypographyVariant</td>
|
||||
<td>Main typography token</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>color</td>
|
||||
<td>TypographyColor</td>
|
||||
<td>color</td>
|
||||
<td>tachTypographyColor</td>
|
||||
<td>Maps to CSS variables</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>weight</td>
|
||||
<td>"normal" | "bold"</td>
|
||||
<td>weight</td>
|
||||
<td>tachTypographyWeight</td>
|
||||
<td>Bold class modifier</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ellipsis</td>
|
||||
<td><code>{"boolean | { rows?: number }"}</code></td>
|
||||
<td>ellipsis (Text/Paragraph)</td>
|
||||
<td>tachTypographyEllipsis</td>
|
||||
<td>Applies line clamp styles</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>clickable</td>
|
||||
<td>boolean</td>
|
||||
<td>onClick adds pointer class</td>
|
||||
<td>tachTypographyClickable</td>
|
||||
<td>Visual affordance + cursor</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>className</td>
|
||||
<td>string</td>
|
||||
<td>className</td>
|
||||
<td>tachTypographyClassName</td>
|
||||
<td>Merges with token classes</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export const AngularAdapter: Story = {
|
||||
render: () => (
|
||||
<div className="tach-story-surface tach-story-stack">
|
||||
<h4>Angular adapter usage</h4>
|
||||
<pre>
|
||||
<code>
|
||||
{`import { TachTypographyDirective, TachTypographyNzModule } from "@hublib-web/tach-typography/angular";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [TachTypographyNzModule, TachTypographyDirective],
|
||||
template: \
|
||||
\`<span
|
||||
nz-typography
|
||||
tachTypography
|
||||
tachTypography="Body"
|
||||
tachTypographyColor="link"
|
||||
tachTypographyWeight="bold"
|
||||
[tachTypographyEllipsis]="{ rows: 2 }"
|
||||
>
|
||||
Typography for Angular + NG-ZORRO
|
||||
</span>\`,
|
||||
})
|
||||
export class ExampleComponent {}
|
||||
`}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -0,0 +1,189 @@
|
||||
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;
|
||||
color?: TypographyColor;
|
||||
ellipsis?: boolean | { rows?: number };
|
||||
href?: string;
|
||||
level?: 1 | 2 | 3 | 4 | 5;
|
||||
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 VariantScaleArgs {
|
||||
namespace: TypographyNamespace;
|
||||
color: TypographyColor;
|
||||
weight: TypographyWeight;
|
||||
sampleText: string;
|
||||
}
|
||||
|
||||
interface ColorPaletteArgs {
|
||||
namespace: TypographyNamespace;
|
||||
variant: TypographyVariant;
|
||||
weight: TypographyWeight;
|
||||
}
|
||||
|
||||
const meta = {
|
||||
title: "TachTypography/Tokens",
|
||||
tags: ["autodocs"],
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"Token showcase for typography variants and colors. Useful for validating visual consistency against the design system.",
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies Meta;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const VariantScale: StoryObj<VariantScaleArgs> = {
|
||||
args: {
|
||||
namespace: "Text",
|
||||
color: "primary",
|
||||
weight: "normal",
|
||||
sampleText: "The quick brown fox jumps over the lazy dog",
|
||||
},
|
||||
argTypes: {
|
||||
namespace: {
|
||||
control: "select",
|
||||
options: ["Text", "Paragraph", "Link", "Title"],
|
||||
},
|
||||
color: {
|
||||
control: "select",
|
||||
options: TYPOGRAPHY_COLORS,
|
||||
},
|
||||
weight: {
|
||||
control: "inline-radio",
|
||||
options: ["normal", "bold"],
|
||||
},
|
||||
sampleText: {
|
||||
control: "text",
|
||||
},
|
||||
},
|
||||
render: args => (
|
||||
<div className="tach-story-surface tach-story-stack">
|
||||
{TYPOGRAPHY_VARIANTS.map(variant => {
|
||||
const Component = getVariantComponent(args.namespace, variant);
|
||||
const componentProps: VariantComponentProps = {
|
||||
color: args.color,
|
||||
weight: args.weight,
|
||||
};
|
||||
|
||||
if (args.namespace === "Link") {
|
||||
componentProps.href = "https://example.com";
|
||||
}
|
||||
|
||||
if (args.namespace === "Title") {
|
||||
componentProps.level = 4;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="tach-story-row" key={variant}>
|
||||
<span className="tach-story-label">{variant}</span>
|
||||
<Component {...componentProps}>{args.sampleText}</Component>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export const ColorPalette: StoryObj<ColorPaletteArgs> = {
|
||||
args: {
|
||||
namespace: "Text",
|
||||
variant: "Body",
|
||||
weight: "normal",
|
||||
},
|
||||
argTypes: {
|
||||
namespace: {
|
||||
control: "select",
|
||||
options: ["Text", "Paragraph", "Link", "Title"],
|
||||
},
|
||||
variant: {
|
||||
control: "select",
|
||||
options: TYPOGRAPHY_VARIANTS,
|
||||
},
|
||||
weight: {
|
||||
control: "inline-radio",
|
||||
options: ["normal", "bold"],
|
||||
},
|
||||
},
|
||||
render: args => {
|
||||
const Component = getVariantComponent(args.namespace, args.variant);
|
||||
|
||||
return (
|
||||
<div className="tach-story-surface tach-story-grid tach-story-grid--colors">
|
||||
{TYPOGRAPHY_COLORS.map(color => {
|
||||
const componentProps: VariantComponentProps = {
|
||||
color,
|
||||
weight: args.weight,
|
||||
};
|
||||
|
||||
if (args.namespace === "Link") {
|
||||
componentProps.href = "https://example.com";
|
||||
}
|
||||
|
||||
if (args.namespace === "Title") {
|
||||
componentProps.level = 4;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="tach-story-row" key={color}>
|
||||
<span className="tach-story-label">{color}</span>
|
||||
<Component {...componentProps}>Color token preview</Component>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const TokenReference: Story = {
|
||||
render: () => (
|
||||
<div className="tach-story-surface tach-story-stack">
|
||||
<div>
|
||||
<h4>Variants ({TYPOGRAPHY_VARIANTS.length})</h4>
|
||||
<ul className="tach-story-token-list">
|
||||
{TYPOGRAPHY_VARIANTS.map(token => (
|
||||
<li key={token}>{token}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>Colors ({TYPOGRAPHY_COLORS.length})</h4>
|
||||
<ul className="tach-story-token-list">
|
||||
{TYPOGRAPHY_COLORS.map(token => (
|
||||
<li key={token}>{token}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
103
packages/tach-typography/src/styles/tach-typography.css
Normal file
103
packages/tach-typography/src/styles/tach-typography.css
Normal file
@@ -0,0 +1,103 @@
|
||||
.tach-typography {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ant-typography.tach-typography {
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.tach-typography--pointer,
|
||||
.ant-typography.tach-typography--pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tach-typography--bold,
|
||||
.ant-typography.tach-typography--bold {
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.tach-typography--color-primary,
|
||||
.ant-typography.tach-typography--color-primary { color: var(--Text-Primary); }
|
||||
.tach-typography--color-secondary,
|
||||
.ant-typography.tach-typography--color-secondary { color: var(--Text-Secondary); }
|
||||
.tach-typography--color-tertiary,
|
||||
.ant-typography.tach-typography--color-tertiary { color: var(--Text-Tertiary); }
|
||||
.tach-typography--color-quaternary,
|
||||
.ant-typography.tach-typography--color-quaternary { color: var(--Text-Quaternary); }
|
||||
.tach-typography--color-link,
|
||||
.ant-typography.tach-typography--color-link { color: var(--System-HashtagsInPost); }
|
||||
.tach-typography--color-white,
|
||||
.ant-typography.tach-typography--color-white { color: var(--Default-White); }
|
||||
.tach-typography--color-dark,
|
||||
.ant-typography.tach-typography--color-dark { color: var(--Default-Dark); }
|
||||
.tach-typography--color-alert,
|
||||
.ant-typography.tach-typography--color-alert { color: var(--System-Alert); }
|
||||
.tach-typography--color-malahit,
|
||||
.ant-typography.tach-typography--color-malahit { color: var(--Accent-Malahit); }
|
||||
.tach-typography--color-attantion,
|
||||
.ant-typography.tach-typography--color-attantion { color: var(--System-Attantion); }
|
||||
|
||||
.tach-typography--LargeTitle,
|
||||
.ant-typography.tach-typography--LargeTitle { font-family: Inter, sans-serif; font-size: 38px; font-weight: 500; line-height: 46px; }
|
||||
.tach-typography--Title1,
|
||||
.ant-typography.tach-typography--Title1 { font-family: Inter, sans-serif; font-size: 28px; font-weight: 500; line-height: 34px; }
|
||||
.tach-typography--Title2,
|
||||
.ant-typography.tach-typography--Title2 { font-family: Inter, sans-serif; font-size: 22px; font-weight: 500; line-height: 28px; }
|
||||
.tach-typography--Title3,
|
||||
.ant-typography.tach-typography--Title3 { font-family: Inter, sans-serif; font-size: 20px; font-weight: 500; line-height: 26px; }
|
||||
.tach-typography--Headline,
|
||||
.ant-typography.tach-typography--Headline { font-family: Inter, sans-serif; font-size: 16px; font-weight: 500; line-height: 24px; }
|
||||
.tach-typography--Body,
|
||||
.tach-typography--AppMediumBody,
|
||||
.ant-typography.tach-typography--Body,
|
||||
.ant-typography.tach-typography--AppMediumBody { font-family: Inter, sans-serif; font-size: 14px; font-weight: 500; line-height: 20px; }
|
||||
.tach-typography--Inputs,
|
||||
.ant-typography.tach-typography--Inputs { font-family: Inter, sans-serif; font-size: 14px; font-weight: 500; line-height: 24px; }
|
||||
.tach-typography--Subheadline,
|
||||
.ant-typography.tach-typography--Subheadline { font-family: Inter, sans-serif; font-size: 14px; font-weight: 500; line-height: 18px; }
|
||||
.tach-typography--FootnoteUnderline,
|
||||
.ant-typography.tach-typography--FootnoteUnderline { font-family: Inter, sans-serif; font-size: 13px; font-weight: 500; line-height: 18px; text-decoration: underline; }
|
||||
.tach-typography--Footnote,
|
||||
.ant-typography.tach-typography--Footnote { font-family: Inter, sans-serif; font-size: 13px; font-weight: 500; line-height: 18px; }
|
||||
.tach-typography--Caption,
|
||||
.ant-typography.tach-typography--Caption { font-family: Inter, sans-serif; font-size: 10px; font-weight: 500; line-height: 12px; text-transform: uppercase; }
|
||||
.tach-typography--Caption2,
|
||||
.ant-typography.tach-typography--Caption2 { font-family: Inter, sans-serif; font-size: 8px; font-weight: 500; line-height: 10px; text-transform: uppercase; }
|
||||
|
||||
.tach-typography--AccentH1,
|
||||
.ant-typography.tach-typography--AccentH1 { font-family: Unbounded, sans-serif; font-size: 20px; font-weight: 700; line-height: 30px; }
|
||||
.tach-typography--AccentH2,
|
||||
.ant-typography.tach-typography--AccentH2 { font-family: Unbounded, sans-serif; font-size: 16px; font-weight: 700; line-height: 24px; }
|
||||
.tach-typography--AccentSubttl,
|
||||
.ant-typography.tach-typography--AccentSubttl { font-family: Unbounded, sans-serif; font-size: 14px; font-weight: 700; line-height: 22px; }
|
||||
.tach-typography--AccentSubttl2,
|
||||
.ant-typography.tach-typography--AccentSubttl2 { font-family: Unbounded, sans-serif; font-size: 12px; font-weight: 700; line-height: 20px; }
|
||||
.tach-typography--AccentCaption,
|
||||
.ant-typography.tach-typography--AccentCaption { font-family: Unbounded, sans-serif; font-size: 9px; font-weight: 700; line-height: 12px; text-transform: uppercase; }
|
||||
.tach-typography--AccentCaption2,
|
||||
.ant-typography.tach-typography--AccentCaption2 { font-family: Unbounded, sans-serif; font-size: 7px; font-weight: 700; line-height: 10px; text-transform: uppercase; }
|
||||
.tach-typography--AccentRegularM,
|
||||
.ant-typography.tach-typography--AccentRegularM { font-family: Unbounded, sans-serif; font-size: 14px; font-weight: 400; line-height: 22px; }
|
||||
.tach-typography--AccentRegularS,
|
||||
.ant-typography.tach-typography--AccentRegularS { font-family: Unbounded, sans-serif; font-size: 12px; font-weight: 400; line-height: 20px; }
|
||||
.tach-typography--AccentLargeTtl,
|
||||
.ant-typography.tach-typography--AccentLargeTtl { font-family: Unbounded, sans-serif; font-size: 38px; font-weight: 700; line-height: 52px; }
|
||||
.tach-typography--AppMediumSubtext,
|
||||
.ant-typography.tach-typography--AppMediumSubtext { text-align: center; font-family: Inter, sans-serif; font-size: 11px; font-weight: 400; line-height: 17px; }
|
||||
.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; }
|
||||
|
||||
@media (max-width: 575px) {
|
||||
.tach-typography--AccentLargeTtl,
|
||||
.ant-typography.tach-typography--AccentLargeTtl {
|
||||
font-size: 20px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.tach-typography--AccentRegularM,
|
||||
.ant-typography.tach-typography--AccentRegularM {
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
262
packages/tach-typography/styles/typography-vars.scss
Normal file
262
packages/tach-typography/styles/typography-vars.scss
Normal file
@@ -0,0 +1,262 @@
|
||||
// Medium styles
|
||||
@mixin AppMediumLargeTitle {
|
||||
/* app/Medium/LargeTitle */
|
||||
font-family: Inter;
|
||||
font-size: 38px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 46px; /* 121.053% */
|
||||
}
|
||||
@mixin AppMediumTitle1 {
|
||||
/* app/Medium/Title1 */
|
||||
font-family: Inter;
|
||||
font-size: 28px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 34px; /* 121.429% */
|
||||
}
|
||||
@mixin AppMediumTitle2 {
|
||||
/* app/Medium/Title2 */
|
||||
font-family: Inter;
|
||||
font-size: 22px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 28px; /* 127.273% */
|
||||
}
|
||||
|
||||
@mixin AppMediumTitle3 {
|
||||
/* app/Medium/Title3 */
|
||||
font-family: Inter;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 26px; /* 130% */
|
||||
}
|
||||
@mixin AppMediumHeadline {
|
||||
/* app/Medium/Headline */
|
||||
font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px; /* 150% */
|
||||
}
|
||||
@mixin AppMediumBody {
|
||||
/* app/Medium/Body */
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
@mixin AppMediumInputs {
|
||||
/* app/Medium/Inputs */
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 24px; /* 171.429% */
|
||||
}
|
||||
@mixin AppMediumSubheadline {
|
||||
/* app/Medium/Subheadline */
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px; /* 128.571% */
|
||||
}
|
||||
@mixin AppMediumFootnoteUnderline {
|
||||
/* app/Medium/Footnote Underline */
|
||||
font-family: Inter;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: solid;
|
||||
text-decoration-skip-ink: none;
|
||||
text-decoration-thickness: auto;
|
||||
text-underline-offset: auto;
|
||||
text-underline-position: from-font;
|
||||
}
|
||||
@mixin AppMediumFootnote {
|
||||
/* app/Medium/footnote */
|
||||
font-family: Inter;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px; /* 138.462% */
|
||||
}
|
||||
@mixin AppMediumCaption {
|
||||
/* app/Medium/Caption */
|
||||
font-family: Inter;
|
||||
font-size: 10px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 12px; /* 120% */
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@mixin AppMediumCaption2 {
|
||||
/* app/Medium/Caption2 */
|
||||
font-family: Inter;
|
||||
font-size: 8px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 10px; /* 125% */
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@mixin AppMediumSubtext {
|
||||
text-align: center;
|
||||
font-family: Inter;
|
||||
font-size: 11px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 17px; /* 154.545% */
|
||||
}
|
||||
|
||||
@mixin AppMediumSubtextUnderline {
|
||||
font-family: Inter;
|
||||
font-size: 11px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 17px;
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: solid;
|
||||
text-decoration-skip-ink: none;
|
||||
text-decoration-thickness: auto;
|
||||
text-underline-offset: auto;
|
||||
text-underline-position: from-font;
|
||||
}
|
||||
|
||||
// Bold Styles
|
||||
@mixin AppBoldLargeTitle {
|
||||
@include AppMediumLargeTitle;
|
||||
font-weight: 700;
|
||||
}
|
||||
@mixin AppBoldTitle1 {
|
||||
@include AppMediumTitle1;
|
||||
font-weight: 700;
|
||||
}
|
||||
@mixin AppBoldTitle2 {
|
||||
@include AppMediumTitle2;
|
||||
font-weight: 700;
|
||||
}
|
||||
@mixin AppBoldTitle3 {
|
||||
@include AppMediumTitle3;
|
||||
font-weight: 700;
|
||||
}
|
||||
@mixin AppBoldHeadline {
|
||||
@include AppMediumHeadline;
|
||||
font-weight: 700;
|
||||
}
|
||||
@mixin AppBoldBody {
|
||||
@include AppMediumBody;
|
||||
font-weight: 700;
|
||||
}
|
||||
@mixin AppBoldInputs {
|
||||
@include AppBoldInputs;
|
||||
font-weight: 700;
|
||||
}
|
||||
@mixin AppBoldSubheadline {
|
||||
@include AppMediumSubheadline;
|
||||
font-weight: 700;
|
||||
}
|
||||
@mixin AppBoldFootnoteUnderline {
|
||||
@include AppMediumFootnoteUnderline;
|
||||
font-weight: 700;
|
||||
}
|
||||
@mixin AppBoldFootnote {
|
||||
@include AppMediumFootnote;
|
||||
font-weight: 700;
|
||||
}
|
||||
@mixin AppBoldCaption {
|
||||
@include AppMediumCaption;
|
||||
font-weight: 700;
|
||||
}
|
||||
@mixin AppBoldCaption2 {
|
||||
@include AppMediumCaption2;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
// Accent Styles
|
||||
@mixin AccentLargeTtl {
|
||||
font-family: Unbounded;
|
||||
font-size: 38px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: 52px; /* 136.842% */
|
||||
|
||||
@media (max-width: 575px) {
|
||||
@include AccentFontH1;
|
||||
}
|
||||
}
|
||||
@mixin AccentFontH1 {
|
||||
/* Accent font/h1 */
|
||||
font-family: Unbounded;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: 30px; /* 150% */
|
||||
}
|
||||
@mixin AccentFontH2 {
|
||||
/* Accent font/h2 */
|
||||
font-family: Unbounded;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: 24px; /* 150% */
|
||||
}
|
||||
@mixin AccentFontSubttl {
|
||||
/* Accent font/subttl */
|
||||
font-family: Unbounded;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: 22px; /* 157.143% */
|
||||
}
|
||||
@mixin AccentFontSubttl2 {
|
||||
/* Accent font/subttl2 */
|
||||
font-family: Unbounded;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: 20px; /* 166.667% */
|
||||
}
|
||||
@mixin AccentFontCaption {
|
||||
/* Accent font/caption */
|
||||
font-family: Unbounded;
|
||||
font-size: 9px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: 12px; /* 133.333% */
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@mixin AccentFontCaption2 {
|
||||
/* Accent font/caption2 */
|
||||
font-family: Unbounded;
|
||||
font-size: 7px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: 10px; /* 142.857% */
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@mixin AccentFontRegularM {
|
||||
/* accent font/regularM */
|
||||
font-family: Unbounded;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px; /* 157.143% */
|
||||
|
||||
@media (max-width: 575px) {
|
||||
@include AccentFontRegularS;
|
||||
}
|
||||
}
|
||||
@mixin AccentFontRegularS {
|
||||
/* accent font/regularS */
|
||||
font-family: Unbounded;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 166.667% */
|
||||
}
|
||||
10
packages/tach-typography/tsconfig.json
Normal file
10
packages/tach-typography/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"types": ["node", "react"]
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
25
packages/tach-typography/tsup.config.ts
Normal file
25
packages/tach-typography/tsup.config.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { defineConfig } from "tsup";
|
||||
|
||||
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,
|
||||
sourcemap: true,
|
||||
clean: false,
|
||||
target: "es2022",
|
||||
minify: false,
|
||||
treeshake: true,
|
||||
splitting: false,
|
||||
external: [
|
||||
"react",
|
||||
"react-dom",
|
||||
"antd",
|
||||
"@angular/core",
|
||||
"@angular/common",
|
||||
"ng-zorro-antd/typography",
|
||||
],
|
||||
});
|
||||
18
packages/video-player/.storybook/main.ts
Normal file
18
packages/video-player/.storybook/main.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { StorybookConfig } from "@storybook/react-vite";
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ["../stories/**/*.stories.@(ts|tsx)"],
|
||||
addons: ["@storybook/addon-essentials"],
|
||||
framework: {
|
||||
name: "@storybook/react-vite",
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: "tag",
|
||||
},
|
||||
core: {
|
||||
disableTelemetry: true,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
13
packages/video-player/.storybook/preview.ts
Normal file
13
packages/video-player/.storybook/preview.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { Preview } from "@storybook/react";
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
layout: "padded",
|
||||
controls: {
|
||||
expanded: true,
|
||||
sort: "requiredFirst",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
||||
103
packages/video-player/README.md
Normal file
103
packages/video-player/README.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# @hublib-web/video-player
|
||||
|
||||
Video player package with shared runtime and framework adapters:
|
||||
|
||||
- `react` entrypoint with SSR-friendly player component
|
||||
- `angular` adapter over framework-agnostic runtime
|
||||
- `core` runtime/utilities for engine selection and token provider
|
||||
|
||||
## Install from Git (SSH tag)
|
||||
|
||||
```bash
|
||||
yarn add "@hublib-web/video-player@git+ssh://git@github.com/ORG/REPO.git#workspace=@hublib-web/video-player&tag=video-player-v0.1.0"
|
||||
```
|
||||
|
||||
## Install inside this monorepo
|
||||
|
||||
```bash
|
||||
yarn add @hublib-web/video-player
|
||||
```
|
||||
|
||||
## Release this package
|
||||
|
||||
1. Bump `version` in `packages/video-player/package.json`.
|
||||
2. Build package artifacts:
|
||||
|
||||
```bash
|
||||
yarn workspace @hublib-web/video-player build
|
||||
```
|
||||
|
||||
3. Commit release files:
|
||||
|
||||
```bash
|
||||
git add packages/video-player/package.json packages/video-player/dist
|
||||
git commit -m "release(video-player): v0.1.0"
|
||||
```
|
||||
|
||||
4. Create and push tag:
|
||||
|
||||
```bash
|
||||
git tag -a video-player-v0.1.0 -m "@hublib-web/video-player v0.1.0"
|
||||
git push origin main --follow-tags
|
||||
```
|
||||
|
||||
Detailed docs:
|
||||
|
||||
- [Release policy](../../docs/release-policy.md)
|
||||
- [Git installation](../../docs/git-installation.md)
|
||||
|
||||
## React usage
|
||||
|
||||
```tsx
|
||||
import VideoPlayer, { type VideoJsPlayer } from "@hublib-web/video-player/react";
|
||||
|
||||
export const Example = () => (
|
||||
<VideoPlayer
|
||||
src="https://example.com/stream.m3u8"
|
||||
aspectRatio="16:9"
|
||||
controls
|
||||
preload="auto"
|
||||
onReady={(player: VideoJsPlayer) => {
|
||||
player.play();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
## Core usage
|
||||
|
||||
```ts
|
||||
import { setVideoPlayerTokenProvider } from "@hublib-web/video-player/core";
|
||||
|
||||
setVideoPlayerTokenProvider(async () => {
|
||||
// host app token resolver
|
||||
return null;
|
||||
});
|
||||
```
|
||||
|
||||
## Angular usage
|
||||
|
||||
```ts
|
||||
import { AngularVideoPlayerAdapter } from "@hublib-web/video-player/angular";
|
||||
|
||||
const adapter = new AngularVideoPlayerAdapter();
|
||||
await adapter.attach(containerElement, {
|
||||
source: { src: "https://example.com/stream.m3u8" },
|
||||
});
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
Run from repository root:
|
||||
|
||||
```bash
|
||||
yarn workspace @hublib-web/video-player build
|
||||
yarn workspace @hublib-web/video-player typecheck
|
||||
yarn workspace @hublib-web/video-player storybook
|
||||
```
|
||||
|
||||
Build static Storybook:
|
||||
|
||||
```bash
|
||||
yarn workspace @hublib-web/video-player storybook:build
|
||||
```
|
||||
26
packages/video-player/dist/angular/index.d.ts
vendored
Normal file
26
packages/video-player/dist/angular/index.d.ts
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import { type VideoPlayerRuntimeEventMap, type VideoPlayerRuntimeInitOptions, type VideoPlayerRuntimeState, type VideoPlayerRuntimeUnsubscribe, type VideoPlayerRuntimeUpdateOptions } from "../core";
|
||||
export interface AngularVideoPlayerInput extends Omit<VideoPlayerRuntimeUpdateOptions, "source"> {
|
||||
source?: VideoPlayerRuntimeInitOptions["source"];
|
||||
}
|
||||
export interface AngularVideoPlayerAttachInput extends Omit<VideoPlayerRuntimeInitOptions, "container"> {
|
||||
}
|
||||
export interface AngularVideoPlayerState {
|
||||
input: AngularVideoPlayerInput;
|
||||
runtime: VideoPlayerRuntimeState;
|
||||
}
|
||||
export declare class AngularVideoPlayerAdapter {
|
||||
private readonly runtime;
|
||||
private input;
|
||||
attach(container: HTMLElement, input: AngularVideoPlayerAttachInput): Promise<{
|
||||
input: AngularVideoPlayerInput;
|
||||
runtime: VideoPlayerRuntimeState;
|
||||
}>;
|
||||
update(nextInput: AngularVideoPlayerInput): Promise<{
|
||||
input: AngularVideoPlayerInput;
|
||||
runtime: VideoPlayerRuntimeState;
|
||||
}>;
|
||||
on<K extends keyof VideoPlayerRuntimeEventMap>(event: K, handler: (payload: VideoPlayerRuntimeEventMap[K]) => void): VideoPlayerRuntimeUnsubscribe;
|
||||
destroy(): void;
|
||||
getState(): AngularVideoPlayerState;
|
||||
}
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
packages/video-player/dist/angular/index.d.ts.map
vendored
Normal file
1
packages/video-player/dist/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":"AAAA,OAAO,EACN,KAAK,0BAA0B,EAC/B,KAAK,6BAA6B,EAClC,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,EAClC,KAAK,+BAA+B,EAEpC,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,uBAChB,SAAQ,IAAI,CAAC,+BAA+B,EAAE,QAAQ,CAAC;IACvD,MAAM,CAAC,EAAE,6BAA6B,CAAC,QAAQ,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,6BAChB,SAAQ,IAAI,CAAC,6BAA6B,EAAE,WAAW,CAAC;CAAG;AAE5D,MAAM,WAAW,uBAAuB;IACvC,KAAK,EAAE,uBAAuB,CAAC;IAC/B,OAAO,EAAE,uBAAuB,CAAC;CACjC;AAGD,qBAAa,yBAAyB;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,KAAK,CAA+B;IAEtC,MAAM,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,6BAA6B;;;;IAcnE,MAAM,CAAC,SAAS,EAAE,uBAAuB;;;;IAc/C,EAAE,CAAC,CAAC,SAAS,MAAM,0BAA0B,EAC5C,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,OAAO,EAAE,0BAA0B,CAAC,CAAC,CAAC,KAAK,IAAI,GACvD,6BAA6B;IAIhC,OAAO;IAIP,QAAQ,IAAI,uBAAuB;CAMnC"}
|
||||
43
packages/video-player/dist/angular/index.js
vendored
Normal file
43
packages/video-player/dist/angular/index.js
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
import { VideoPlayerRuntime, } from "../core";
|
||||
// Angular-friendly wrapper around framework-agnostic runtime.
|
||||
export class AngularVideoPlayerAdapter {
|
||||
constructor() {
|
||||
this.runtime = new VideoPlayerRuntime();
|
||||
this.input = {};
|
||||
}
|
||||
async attach(container, input) {
|
||||
this.input = { ...input };
|
||||
const runtime = await this.runtime.init({
|
||||
container,
|
||||
...input,
|
||||
});
|
||||
return {
|
||||
input: this.input,
|
||||
runtime,
|
||||
};
|
||||
}
|
||||
async update(nextInput) {
|
||||
this.input = {
|
||||
...this.input,
|
||||
...nextInput,
|
||||
};
|
||||
const runtime = await this.runtime.update(nextInput);
|
||||
return {
|
||||
input: this.input,
|
||||
runtime,
|
||||
};
|
||||
}
|
||||
on(event, handler) {
|
||||
return this.runtime.on(event, handler);
|
||||
}
|
||||
destroy() {
|
||||
this.runtime.dispose();
|
||||
}
|
||||
getState() {
|
||||
return {
|
||||
input: this.input,
|
||||
runtime: this.runtime.getState(),
|
||||
};
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/video-player/dist/angular/index.js.map
vendored
Normal file
1
packages/video-player/dist/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,EAMN,kBAAkB,GAClB,MAAM,SAAS,CAAC;AAejB,8DAA8D;AAC9D,MAAM,OAAO,yBAAyB;IAAtC;QACkB,YAAO,GAAG,IAAI,kBAAkB,EAAE,CAAC;QAC5C,UAAK,GAA4B,EAAE,CAAC;IA+C7C,CAAC;IA7CA,KAAK,CAAC,MAAM,CAAC,SAAsB,EAAE,KAAoC;QACxE,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAE1B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;YACvC,SAAS;YACT,GAAG,KAAK;SACR,CAAC,CAAC;QAEH,OAAO;YACN,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO;SAC2B,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,SAAkC;QAC9C,IAAI,CAAC,KAAK,GAAG;YACZ,GAAG,IAAI,CAAC,KAAK;YACb,GAAG,SAAS;SACZ,CAAC;QAEF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAErD,OAAO;YACN,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO;SAC2B,CAAC;IACrC,CAAC;IAED,EAAE,CACD,KAAQ,EACR,OAAyD;QAEzD,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,OAAO;QACN,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;IAED,QAAQ;QACP,OAAO;YACN,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE;SAChC,CAAC;IACH,CAAC;CACD"}
|
||||
12
packages/video-player/dist/core/engine-selector.d.ts
vendored
Normal file
12
packages/video-player/dist/core/engine-selector.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
export type PlaybackEngine = "hls" | "videojs";
|
||||
export type EngineStrategy = "auto" | "force-hls" | "force-videojs";
|
||||
export interface EngineSelectionInput {
|
||||
src?: string | null;
|
||||
type?: string | null;
|
||||
strategy?: EngineStrategy;
|
||||
hlsSupported?: boolean;
|
||||
isIOS?: boolean;
|
||||
}
|
||||
export declare const isHlsSource: ({ src, type }: Pick<EngineSelectionInput, "src" | "type">) => boolean;
|
||||
export declare const selectPlaybackEngine: ({ src, type, strategy, hlsSupported, isIOS, }: EngineSelectionInput) => PlaybackEngine;
|
||||
//# sourceMappingURL=engine-selector.d.ts.map
|
||||
1
packages/video-player/dist/core/engine-selector.d.ts.map
vendored
Normal file
1
packages/video-player/dist/core/engine-selector.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"engine-selector.d.ts","sourceRoot":"","sources":["../../src/core/engine-selector.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,SAAS,CAAC;AAE/C,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,WAAW,GAAG,eAAe,CAAC;AAEpE,MAAM,WAAW,oBAAoB;IACpC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAOD,eAAO,MAAM,WAAW,GAAI,eAAe,IAAI,CAAC,oBAAoB,EAAE,KAAK,GAAG,MAAM,CAAC,KAAG,OAIvF,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,+CAMlC,oBAAoB,KAAG,cAkBzB,CAAC"}
|
||||
24
packages/video-player/dist/core/engine-selector.js
vendored
Normal file
24
packages/video-player/dist/core/engine-selector.js
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
const HLS_MIME_TYPES = new Set([
|
||||
"application/x-mpegurl",
|
||||
"application/vnd.apple.mpegurl",
|
||||
]);
|
||||
export const isHlsSource = ({ src, type }) => {
|
||||
const sourceType = (type || "").toLowerCase();
|
||||
return HLS_MIME_TYPES.has(sourceType) || /\.m3u8($|\?)/i.test(src || "");
|
||||
};
|
||||
export const selectPlaybackEngine = ({ src, type, strategy = "auto", hlsSupported = false, isIOS = false, }) => {
|
||||
if (strategy === "force-hls") {
|
||||
return "hls";
|
||||
}
|
||||
if (strategy === "force-videojs") {
|
||||
return "videojs";
|
||||
}
|
||||
if (!isHlsSource({ src, type })) {
|
||||
return "videojs";
|
||||
}
|
||||
if (isIOS) {
|
||||
return "videojs";
|
||||
}
|
||||
return hlsSupported ? "hls" : "videojs";
|
||||
};
|
||||
//# sourceMappingURL=engine-selector.js.map
|
||||
1
packages/video-player/dist/core/engine-selector.js.map
vendored
Normal file
1
packages/video-player/dist/core/engine-selector.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"engine-selector.js","sourceRoot":"","sources":["../../src/core/engine-selector.ts"],"names":[],"mappings":"AAYA,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC9B,uBAAuB;IACvB,+BAA+B;CAC/B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,EAA8C,EAAW,EAAE;IACjG,MAAM,UAAU,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAE9C,OAAO,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;AAC1E,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,EACpC,GAAG,EACH,IAAI,EACJ,QAAQ,GAAG,MAAM,EACjB,YAAY,GAAG,KAAK,EACpB,KAAK,GAAG,KAAK,GACS,EAAkB,EAAE;IAC1C,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACd,CAAC;IAED,IAAI,QAAQ,KAAK,eAAe,EAAE,CAAC;QAClC,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACjC,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,IAAI,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,OAAO,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACzC,CAAC,CAAC"}
|
||||
2
packages/video-player/dist/core/format-time.d.ts
vendored
Normal file
2
packages/video-player/dist/core/format-time.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export declare const formatTime: (seconds: number) => string;
|
||||
//# sourceMappingURL=format-time.d.ts.map
|
||||
1
packages/video-player/dist/core/format-time.d.ts.map
vendored
Normal file
1
packages/video-player/dist/core/format-time.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"format-time.d.ts","sourceRoot":"","sources":["../../src/core/format-time.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,WAWzC,CAAC"}
|
||||
11
packages/video-player/dist/core/format-time.js
vendored
Normal file
11
packages/video-player/dist/core/format-time.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
export const formatTime = (seconds) => {
|
||||
const pad = (num) => String(num).padStart(2, "0");
|
||||
const hrs = Math.floor(seconds / 3600);
|
||||
const mins = Math.floor((seconds % 3600) / 60);
|
||||
const secs = Math.floor(seconds % 60);
|
||||
if (seconds < 3600) {
|
||||
return `${pad(mins)}:${pad(secs)}`;
|
||||
}
|
||||
return `${pad(hrs)}:${pad(mins)}:${pad(secs)}`;
|
||||
};
|
||||
//# sourceMappingURL=format-time.js.map
|
||||
1
packages/video-player/dist/core/format-time.js.map
vendored
Normal file
1
packages/video-player/dist/core/format-time.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"format-time.js","sourceRoot":"","sources":["../../src/core/format-time.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,EAAE;IAC7C,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IAEtC,IAAI,OAAO,GAAG,IAAI,EAAE,CAAC;QACpB,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;IACpC,CAAC;IAED,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;AAChD,CAAC,CAAC"}
|
||||
4
packages/video-player/dist/core/index.d.ts
vendored
Normal file
4
packages/video-player/dist/core/index.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export { isHlsSource, selectPlaybackEngine, type EngineSelectionInput, type EngineStrategy, type PlaybackEngine, } from "./engine-selector";
|
||||
export { resolveVideoPlayerToken, setVideoPlayerTokenProvider, type VideoPlayerToken, type VideoPlayerTokenProvider, } from "./token-provider";
|
||||
export { VideoPlayerRuntime, type VideoPlayerRuntimeEventMap, type VideoPlayerRuntimeInitOptions, type VideoPlayerRuntimePlayer, type VideoPlayerRuntimePreload, type VideoPlayerRuntimeSource, type VideoPlayerRuntimeState, type VideoPlayerRuntimeUnsubscribe, type VideoPlayerRuntimeUpdateOptions, } from "./player-runtime";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
packages/video-player/dist/core/index.d.ts.map
vendored
Normal file
1
packages/video-player/dist/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,OAAO,EACN,WAAW,EACX,oBAAoB,EACpB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,cAAc,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,uBAAuB,EACvB,2BAA2B,EAC3B,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,GAC7B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACN,kBAAkB,EAClB,KAAK,0BAA0B,EAC/B,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,EAClC,KAAK,+BAA+B,GACpC,MAAM,kBAAkB,CAAC"}
|
||||
4
packages/video-player/dist/core/index.js
vendored
Normal file
4
packages/video-player/dist/core/index.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export { isHlsSource, selectPlaybackEngine, } from "./engine-selector";
|
||||
export { resolveVideoPlayerToken, setVideoPlayerTokenProvider, } from "./token-provider";
|
||||
export { VideoPlayerRuntime, } from "./player-runtime";
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/video-player/dist/core/index.js.map
vendored
Normal file
1
packages/video-player/dist/core/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,WAAW,EACX,oBAAoB,GAIpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,uBAAuB,EACvB,2BAA2B,GAG3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACN,kBAAkB,GASlB,MAAM,kBAAkB,CAAC"}
|
||||
137
packages/video-player/dist/core/player-runtime.d.ts
vendored
Normal file
137
packages/video-player/dist/core/player-runtime.d.ts
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
import Hls from "hls.js";
|
||||
import type Player from "video.js/dist/types/player";
|
||||
import "./plugins/register";
|
||||
import { type EngineStrategy, type PlaybackEngine } from "./engine-selector";
|
||||
export interface VideoPlayerRuntimeSource {
|
||||
src: string;
|
||||
type?: string;
|
||||
}
|
||||
export type VideoPlayerRuntimePreload = "auto" | "metadata" | "none" | "visibility";
|
||||
export type VideoPlayerRuntimeEventMap = {
|
||||
ready: {
|
||||
engine: PlaybackEngine;
|
||||
source: VideoPlayerRuntimeSource;
|
||||
player: VideoPlayerRuntimePlayer;
|
||||
};
|
||||
enginechange: {
|
||||
previous: PlaybackEngine | null;
|
||||
next: PlaybackEngine;
|
||||
source: VideoPlayerRuntimeSource;
|
||||
};
|
||||
sourcechange: {
|
||||
previous: VideoPlayerRuntimeSource;
|
||||
next: VideoPlayerRuntimeSource;
|
||||
engine: PlaybackEngine;
|
||||
};
|
||||
manifestloaded: {
|
||||
engine: PlaybackEngine;
|
||||
duration?: number;
|
||||
live?: boolean;
|
||||
};
|
||||
duration: {
|
||||
duration: number;
|
||||
};
|
||||
playstart: {
|
||||
engine: PlaybackEngine;
|
||||
};
|
||||
error: {
|
||||
scope: "runtime" | "player" | "hls";
|
||||
error: unknown;
|
||||
fatal?: boolean;
|
||||
};
|
||||
dispose: Record<string, never>;
|
||||
};
|
||||
export type VideoPlayerRuntimePlayer = Player & {
|
||||
hlsInstance?: Hls | null;
|
||||
liveTracker?: {
|
||||
isLive_: boolean;
|
||||
atLiveEdge?: () => boolean;
|
||||
startTracking: () => void;
|
||||
trigger: (event: string) => void;
|
||||
};
|
||||
settingsMenu?: () => void;
|
||||
bigPlayPauseButton?: () => void;
|
||||
skipButtons?: (options: {
|
||||
skip: number;
|
||||
}) => void;
|
||||
subscribeToSegmentChange: (callback: (segment: unknown) => void) => void;
|
||||
subscribeToDuration: (callback: (duration: number) => void) => void;
|
||||
subscribeToPlayStart: (callback: () => void) => void;
|
||||
subscribeToPlayStarted: (callback: () => void) => void;
|
||||
subscribeToManifestLoaded: (callback: () => void) => void;
|
||||
mediaduration: () => number | undefined;
|
||||
};
|
||||
interface VideoPlayerRuntimeOptions {
|
||||
source: VideoPlayerRuntimeSource;
|
||||
strategy: EngineStrategy;
|
||||
preload: VideoPlayerRuntimePreload;
|
||||
autoplay: boolean;
|
||||
controls: boolean;
|
||||
responsive: boolean;
|
||||
aspectRatio?: string;
|
||||
fluid: boolean;
|
||||
muted: boolean;
|
||||
poster?: string;
|
||||
preferHQ: boolean;
|
||||
debug: boolean;
|
||||
initialTime: number;
|
||||
isIOS?: boolean;
|
||||
isMobile?: boolean;
|
||||
full: boolean;
|
||||
withRewind: boolean;
|
||||
skipSeconds: number;
|
||||
classNames: string[];
|
||||
onPlayerReady?: (player: VideoPlayerRuntimePlayer, state: VideoPlayerRuntimeState) => void;
|
||||
}
|
||||
export interface VideoPlayerRuntimeInitOptions extends Partial<Omit<VideoPlayerRuntimeOptions, "source">> {
|
||||
container: HTMLElement;
|
||||
source: VideoPlayerRuntimeSource;
|
||||
}
|
||||
export interface VideoPlayerRuntimeUpdateOptions extends Partial<Omit<VideoPlayerRuntimeOptions, "source">> {
|
||||
source?: VideoPlayerRuntimeSource;
|
||||
}
|
||||
export interface VideoPlayerRuntimeState {
|
||||
initialized: boolean;
|
||||
engine: PlaybackEngine | null;
|
||||
source: VideoPlayerRuntimeSource | null;
|
||||
}
|
||||
export type VideoPlayerRuntimeUnsubscribe = () => void;
|
||||
export declare class VideoPlayerRuntime {
|
||||
private containerRef;
|
||||
private videoRef;
|
||||
private playerRef;
|
||||
private hlsRef;
|
||||
private options;
|
||||
private currentEngine;
|
||||
private currentSource;
|
||||
private vhsAuthTokenRef;
|
||||
private vhsRequestCleanupRef;
|
||||
private visibilityObserverRef;
|
||||
private originalPlayRef;
|
||||
private hlsLoaded;
|
||||
private eventListeners;
|
||||
init(options: VideoPlayerRuntimeInitOptions): Promise<VideoPlayerRuntimeState>;
|
||||
update(options: VideoPlayerRuntimeUpdateOptions): Promise<VideoPlayerRuntimeState>;
|
||||
on<K extends keyof VideoPlayerRuntimeEventMap>(event: K, handler: (payload: VideoPlayerRuntimeEventMap[K]) => void): VideoPlayerRuntimeUnsubscribe;
|
||||
dispose(): void;
|
||||
getState(): VideoPlayerRuntimeState;
|
||||
getPlayer(): VideoPlayerRuntimePlayer | null;
|
||||
private emit;
|
||||
private tryPlay;
|
||||
private resolveEngine;
|
||||
private createVideoElement;
|
||||
private createPlayer;
|
||||
private syncPoster;
|
||||
private syncPlayerDisplayOptions;
|
||||
private attachCompatibilityApi;
|
||||
private attachDurationAndPlayStartHooks;
|
||||
private loadCurrentSource;
|
||||
private buildHlsConfig;
|
||||
private loadHlsSource;
|
||||
private loadVideoJsSource;
|
||||
private resetDeferredHlsLoading;
|
||||
private ensureVhsAuthInterceptor;
|
||||
private teardownHls;
|
||||
}
|
||||
export {};
|
||||
//# sourceMappingURL=player-runtime.d.ts.map
|
||||
1
packages/video-player/dist/core/player-runtime.d.ts.map
vendored
Normal file
1
packages/video-player/dist/core/player-runtime.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"player-runtime.d.ts","sourceRoot":"","sources":["../../src/core/player-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,GAON,MAAM,QAAQ,CAAC;AAEhB,OAAO,KAAK,MAAM,MAAM,4BAA4B,CAAC;AAErD,OAAO,oBAAoB,CAAC;AAE5B,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,cAAc,EAEnB,MAAM,mBAAmB,CAAC;AAI3B,MAAM,WAAW,wBAAwB;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,yBAAyB,GAClC,MAAM,GACN,UAAU,GACV,MAAM,GACN,YAAY,CAAC;AAEhB,MAAM,MAAM,0BAA0B,GAAG;IACxC,KAAK,EAAE;QACN,MAAM,EAAE,cAAc,CAAC;QACvB,MAAM,EAAE,wBAAwB,CAAC;QACjC,MAAM,EAAE,wBAAwB,CAAC;KACjC,CAAC;IACF,YAAY,EAAE;QACb,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;QAChC,IAAI,EAAE,cAAc,CAAC;QACrB,MAAM,EAAE,wBAAwB,CAAC;KACjC,CAAC;IACF,YAAY,EAAE;QACb,QAAQ,EAAE,wBAAwB,CAAC;QACnC,IAAI,EAAE,wBAAwB,CAAC;QAC/B,MAAM,EAAE,cAAc,CAAC;KACvB,CAAC;IACF,cAAc,EAAE;QACf,MAAM,EAAE,cAAc,CAAC;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,OAAO,CAAC;KACf,CAAC;IACF,QAAQ,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,SAAS,EAAE;QACV,MAAM,EAAE,cAAc,CAAC;KACvB,CAAC;IACF,KAAK,EAAE;QACN,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC;QACpC,KAAK,EAAE,OAAO,CAAC;QACf,KAAK,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;IACF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG;IAC/C,WAAW,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE;QACb,OAAO,EAAE,OAAO,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;QAC3B,aAAa,EAAE,MAAM,IAAI,CAAC;QAC1B,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;KACjC,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAClD,wBAAwB,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,CAAC;IACzE,mBAAmB,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,KAAK,IAAI,CAAC;IACpE,oBAAoB,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACrD,sBAAsB,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACvD,yBAAyB,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IAC1D,aAAa,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;CACxC,CAAC;AAEF,UAAU,yBAAyB;IAClC,MAAM,EAAE,wBAAwB,CAAC;IACjC,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,yBAAyB,CAAC;IACnC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,CAAC,EAAE,CACf,MAAM,EAAE,wBAAwB,EAChC,KAAK,EAAE,uBAAuB,KAC1B,IAAI,CAAC;CACV;AAED,MAAM,WAAW,6BAChB,SAAQ,OAAO,CAAC,IAAI,CAAC,yBAAyB,EAAE,QAAQ,CAAC,CAAC;IAC1D,SAAS,EAAE,WAAW,CAAC;IACvB,MAAM,EAAE,wBAAwB,CAAC;CACjC;AAED,MAAM,WAAW,+BAChB,SAAQ,OAAO,CAAC,IAAI,CAAC,yBAAyB,EAAE,QAAQ,CAAC,CAAC;IAC1D,MAAM,CAAC,EAAE,wBAAwB,CAAC;CAClC;AAED,MAAM,WAAW,uBAAuB;IACvC,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,wBAAwB,GAAG,IAAI,CAAC;CACxC;AAED,MAAM,MAAM,6BAA6B,GAAG,MAAM,IAAI,CAAC;AAgGvD,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,OAAO,CAA0C;IACzD,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,aAAa,CAAyC;IAC9D,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,oBAAoB,CAA6B;IACzD,OAAO,CAAC,qBAAqB,CAAqC;IAClE,OAAO,CAAC,eAAe,CAAiD;IACxE,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAGlB;IAEE,IAAI,CAAC,OAAO,EAAE,6BAA6B;IAwB3C,MAAM,CAAC,OAAO,EAAE,+BAA+B;IAqCrD,EAAE,CAAC,CAAC,SAAS,MAAM,0BAA0B,EAC5C,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,OAAO,EAAE,0BAA0B,CAAC,CAAC,CAAC,KAAK,IAAI,GACvD,6BAA6B;IAehC,OAAO;IA0BP,QAAQ,IAAI,uBAAuB;IAQnC,SAAS;IAIT,OAAO,CAAC,IAAI;IAqBZ,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,kBAAkB;IA0B1B,OAAO,CAAC,YAAY;IAuCpB,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,wBAAwB;IAkChC,OAAO,CAAC,sBAAsB;IA0F9B,OAAO,CAAC,+BAA+B;YAoBzB,iBAAiB;IA8B/B,OAAO,CAAC,cAAc;YAoCR,aAAa;YAgMb,iBAAiB;IAwC/B,OAAO,CAAC,uBAAuB;IAY/B,OAAO,CAAC,wBAAwB;IAqDhC,OAAO,CAAC,WAAW;CAuBnB"}
|
||||
707
packages/video-player/dist/core/player-runtime.js
vendored
Normal file
707
packages/video-player/dist/core/player-runtime.js
vendored
Normal file
@@ -0,0 +1,707 @@
|
||||
import Hls from "hls.js";
|
||||
import videojs from "video.js";
|
||||
import "./plugins/register";
|
||||
import { selectPlaybackEngine, } from "./engine-selector";
|
||||
import { formatTime } from "./format-time";
|
||||
import { resolveVideoPlayerToken } from "./token-provider";
|
||||
const DEFAULT_SOURCE_TYPE = "application/x-mpegurl";
|
||||
const detectIOS = () => {
|
||||
if (typeof navigator === "undefined") {
|
||||
return false;
|
||||
}
|
||||
const userAgent = navigator.userAgent || "";
|
||||
return /iPad|iPhone|iPod/.test(userAgent);
|
||||
};
|
||||
const createAuthPlaylistLoader = ({ debug, }) => {
|
||||
const BaseLoader = Hls.DefaultConfig.loader;
|
||||
return class AuthPlaylistLoader extends BaseLoader {
|
||||
constructor(config) {
|
||||
super({ ...config, debug: debug ?? false });
|
||||
}
|
||||
load(context, config, callbacks) {
|
||||
const start = async () => {
|
||||
try {
|
||||
const token = await resolveVideoPlayerToken();
|
||||
if (token) {
|
||||
context.headers = {
|
||||
...(context.headers ?? {}),
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
if (debug) {
|
||||
console.warn("[VideoRuntime:HLS] Failed to append auth header to playlist request", error);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
super.load(context, config, callbacks);
|
||||
}
|
||||
};
|
||||
void start().catch(error => {
|
||||
if (debug) {
|
||||
console.error("[VideoRuntime:HLS] Playlist loader start failed", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
const normalizeSource = (source) => ({
|
||||
src: source?.src ?? "",
|
||||
type: source?.type ?? DEFAULT_SOURCE_TYPE,
|
||||
});
|
||||
const normalizeOptions = (options, previous) => ({
|
||||
source: normalizeSource(options.source ?? previous?.source),
|
||||
strategy: options.strategy ?? previous?.strategy ?? "auto",
|
||||
preload: options.preload ?? previous?.preload ?? "auto",
|
||||
autoplay: options.autoplay ?? previous?.autoplay ?? false,
|
||||
controls: options.controls ?? previous?.controls ?? true,
|
||||
responsive: options.responsive ?? previous?.responsive ?? true,
|
||||
aspectRatio: options.aspectRatio ?? previous?.aspectRatio,
|
||||
fluid: options.fluid ?? previous?.fluid ?? true,
|
||||
muted: options.muted ?? previous?.muted ?? false,
|
||||
poster: options.poster ?? previous?.poster,
|
||||
preferHQ: options.preferHQ ?? previous?.preferHQ ?? false,
|
||||
debug: options.debug ?? previous?.debug ?? false,
|
||||
initialTime: options.initialTime ?? previous?.initialTime ?? 0,
|
||||
isIOS: options.isIOS ?? previous?.isIOS,
|
||||
isMobile: options.isMobile ?? previous?.isMobile,
|
||||
full: options.full ?? previous?.full ?? false,
|
||||
withRewind: options.withRewind ?? previous?.withRewind ?? true,
|
||||
skipSeconds: options.skipSeconds ?? previous?.skipSeconds ?? 10,
|
||||
classNames: options.classNames ?? previous?.classNames ?? [],
|
||||
onPlayerReady: options.onPlayerReady ?? previous?.onPlayerReady,
|
||||
});
|
||||
export class VideoPlayerRuntime {
|
||||
constructor() {
|
||||
this.containerRef = null;
|
||||
this.videoRef = null;
|
||||
this.playerRef = null;
|
||||
this.hlsRef = null;
|
||||
this.options = null;
|
||||
this.currentEngine = null;
|
||||
this.currentSource = null;
|
||||
this.vhsAuthTokenRef = null;
|
||||
this.vhsRequestCleanupRef = null;
|
||||
this.visibilityObserverRef = null;
|
||||
this.originalPlayRef = null;
|
||||
this.hlsLoaded = false;
|
||||
this.eventListeners = new Map();
|
||||
}
|
||||
async init(options) {
|
||||
this.dispose();
|
||||
this.containerRef = options.container;
|
||||
this.options = normalizeOptions(options);
|
||||
this.createVideoElement();
|
||||
this.createPlayer();
|
||||
const state = await this.loadCurrentSource(null);
|
||||
this.emit("ready", {
|
||||
engine: state.engine,
|
||||
source: state.source,
|
||||
player: this.playerRef,
|
||||
});
|
||||
this.options.onPlayerReady?.(this.playerRef, state);
|
||||
return state;
|
||||
}
|
||||
async update(options) {
|
||||
if (!this.options || !this.playerRef) {
|
||||
return this.getState();
|
||||
}
|
||||
const previous = this.options;
|
||||
const nextOptions = normalizeOptions(options, previous);
|
||||
this.options = nextOptions;
|
||||
this.syncPlayerDisplayOptions(previous, nextOptions);
|
||||
const sourceChanged = nextOptions.source.src !== previous.source.src ||
|
||||
nextOptions.source.type !== previous.source.type;
|
||||
const engineDependsOnChanged = nextOptions.strategy !== previous.strategy ||
|
||||
nextOptions.isIOS !== previous.isIOS;
|
||||
if (sourceChanged || engineDependsOnChanged) {
|
||||
if (sourceChanged) {
|
||||
this.emit("sourcechange", {
|
||||
previous: previous.source,
|
||||
next: nextOptions.source,
|
||||
engine: this.currentEngine,
|
||||
});
|
||||
}
|
||||
return this.loadCurrentSource(this.currentEngine);
|
||||
}
|
||||
if (nextOptions.poster !== previous.poster) {
|
||||
this.syncPoster(nextOptions.poster);
|
||||
}
|
||||
return this.getState();
|
||||
}
|
||||
on(event, handler) {
|
||||
if (!this.eventListeners.has(event)) {
|
||||
this.eventListeners.set(event, new Set());
|
||||
}
|
||||
const listeners = this.eventListeners.get(event);
|
||||
listeners.add(handler);
|
||||
return () => {
|
||||
listeners.delete(handler);
|
||||
};
|
||||
}
|
||||
dispose() {
|
||||
this.emit("dispose", {});
|
||||
this.resetDeferredHlsLoading();
|
||||
this.teardownHls();
|
||||
this.vhsRequestCleanupRef?.();
|
||||
this.vhsRequestCleanupRef = null;
|
||||
this.vhsAuthTokenRef = null;
|
||||
if (this.playerRef) {
|
||||
this.playerRef.dispose();
|
||||
}
|
||||
if (this.containerRef) {
|
||||
this.containerRef.innerHTML = "";
|
||||
}
|
||||
this.containerRef = null;
|
||||
this.videoRef = null;
|
||||
this.playerRef = null;
|
||||
this.options = null;
|
||||
this.currentEngine = null;
|
||||
this.currentSource = null;
|
||||
this.hlsLoaded = false;
|
||||
}
|
||||
getState() {
|
||||
return {
|
||||
initialized: Boolean(this.playerRef),
|
||||
engine: this.currentEngine,
|
||||
source: this.currentSource,
|
||||
};
|
||||
}
|
||||
getPlayer() {
|
||||
return this.playerRef;
|
||||
}
|
||||
emit(event, payload) {
|
||||
const listeners = this.eventListeners.get(event);
|
||||
if (!listeners?.size) {
|
||||
return;
|
||||
}
|
||||
listeners.forEach(listener => {
|
||||
try {
|
||||
listener(payload);
|
||||
}
|
||||
catch (error) {
|
||||
console.error("[VideoRuntime] Listener failed", {
|
||||
event,
|
||||
error,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
tryPlay(player) {
|
||||
const playResult = player.play();
|
||||
if (playResult && typeof playResult.catch === "function") {
|
||||
void playResult.catch(() => undefined);
|
||||
}
|
||||
}
|
||||
resolveEngine(options) {
|
||||
return selectPlaybackEngine({
|
||||
src: options.source.src,
|
||||
type: options.source.type,
|
||||
strategy: options.strategy,
|
||||
hlsSupported: Hls.isSupported(),
|
||||
isIOS: options.isIOS ?? detectIOS(),
|
||||
});
|
||||
}
|
||||
createVideoElement() {
|
||||
if (!this.containerRef || !this.options) {
|
||||
return;
|
||||
}
|
||||
const videoElement = document.createElement("video");
|
||||
videoElement.classList.add("video-js", "vjs-tach-skin");
|
||||
if (this.options.isMobile) {
|
||||
videoElement.classList.add("vjs-mobile-ui");
|
||||
}
|
||||
if (!this.options.withRewind) {
|
||||
videoElement.classList.add("vjs-disable-rewind");
|
||||
}
|
||||
videoElement.setAttribute("playsinline", "true");
|
||||
if (this.options.poster) {
|
||||
videoElement.setAttribute("poster", this.options.poster);
|
||||
}
|
||||
this.options.classNames.forEach(className => {
|
||||
videoElement.classList.add(className);
|
||||
});
|
||||
this.containerRef.innerHTML = "";
|
||||
this.containerRef.appendChild(videoElement);
|
||||
this.videoRef = videoElement;
|
||||
}
|
||||
createPlayer() {
|
||||
if (!this.videoRef || !this.options) {
|
||||
throw new Error("[VideoRuntime] Unable to create player without video element");
|
||||
}
|
||||
const videoJsAny = videojs;
|
||||
videoJsAny.formatTime = formatTime;
|
||||
videoJsAny.setFormatTime?.(formatTime);
|
||||
const player = (this.playerRef = videojs(this.videoRef, {
|
||||
autoplay: this.options.autoplay,
|
||||
controls: this.options.controls,
|
||||
preload: this.options.preload === "visibility" ? "none" : this.options.preload,
|
||||
fluid: this.options.fluid,
|
||||
responsive: this.options.responsive,
|
||||
aspectRatio: this.options.aspectRatio,
|
||||
muted: this.options.muted,
|
||||
}));
|
||||
this.attachCompatibilityApi(player);
|
||||
player.on("error", () => {
|
||||
this.emit("error", {
|
||||
scope: "player",
|
||||
error: player.error?.() ?? new Error("Unknown player error"),
|
||||
});
|
||||
});
|
||||
player.bigPlayPauseButton?.();
|
||||
if (this.options.full) {
|
||||
player.settingsMenu?.();
|
||||
player.skipButtons?.({ skip: this.options.skipSeconds });
|
||||
}
|
||||
}
|
||||
syncPoster(poster) {
|
||||
const videoElement = this.videoRef;
|
||||
if (!videoElement) {
|
||||
return;
|
||||
}
|
||||
if (poster) {
|
||||
videoElement.setAttribute("poster", poster);
|
||||
}
|
||||
else {
|
||||
videoElement.removeAttribute("poster");
|
||||
}
|
||||
}
|
||||
syncPlayerDisplayOptions(previous, next) {
|
||||
const player = this.playerRef;
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
if (previous.muted !== next.muted) {
|
||||
player.muted(next.muted);
|
||||
}
|
||||
if (previous.controls !== next.controls) {
|
||||
player.controls(next.controls);
|
||||
}
|
||||
if (!previous.full && next.full) {
|
||||
player.settingsMenu?.();
|
||||
player.skipButtons?.({ skip: next.skipSeconds });
|
||||
}
|
||||
if (previous.initialTime !== next.initialTime && next.initialTime > 0) {
|
||||
if (this.hlsRef) {
|
||||
this.hlsRef.startLoad(next.initialTime);
|
||||
}
|
||||
player.currentTime(next.initialTime);
|
||||
}
|
||||
if (!previous.autoplay && next.autoplay && player.paused()) {
|
||||
this.tryPlay(player);
|
||||
}
|
||||
}
|
||||
attachCompatibilityApi(player) {
|
||||
player.subscribeToSegmentChange = callback => {
|
||||
let lastIndex = -1;
|
||||
if (this.hlsRef) {
|
||||
this.hlsRef.on(Hls.Events.FRAG_CHANGED, (_event, data) => {
|
||||
callback(data.frag);
|
||||
});
|
||||
return;
|
||||
}
|
||||
player.on("timeupdate", () => {
|
||||
const seconds = Math.floor(player.currentTime() || 0);
|
||||
const segmentIndex = Math.floor(seconds / 10);
|
||||
if (segmentIndex > lastIndex) {
|
||||
lastIndex = segmentIndex;
|
||||
callback({ start: segmentIndex * 10 });
|
||||
}
|
||||
});
|
||||
};
|
||||
player.mediaduration = () => {
|
||||
if (this.hlsRef) {
|
||||
const level = this.hlsRef.levels[this.hlsRef.currentLevel];
|
||||
const details = level?.details;
|
||||
if (details?.totalduration) {
|
||||
return details.totalduration;
|
||||
}
|
||||
}
|
||||
return player.duration();
|
||||
};
|
||||
player.subscribeToDuration = callback => {
|
||||
const fire = (duration) => {
|
||||
if (duration && !Number.isNaN(duration) && duration > 0) {
|
||||
callback(duration);
|
||||
}
|
||||
};
|
||||
fire(player.duration());
|
||||
if (this.hlsRef) {
|
||||
this.hlsRef.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
const totalDuration = this.hlsRef?.levels[0]?.details?.totalduration;
|
||||
if (totalDuration) {
|
||||
fire(totalDuration);
|
||||
}
|
||||
});
|
||||
}
|
||||
player.on("loadedmetadata", () => fire(player.mediaduration()));
|
||||
};
|
||||
player.subscribeToPlayStart = callback => {
|
||||
let started = false;
|
||||
const onPlayStart = () => {
|
||||
if (!started) {
|
||||
started = true;
|
||||
callback();
|
||||
}
|
||||
};
|
||||
if (this.hlsRef) {
|
||||
this.hlsRef.once(Hls.Events.FRAG_BUFFERED, onPlayStart);
|
||||
}
|
||||
player.one("playing", onPlayStart);
|
||||
};
|
||||
player.subscribeToPlayStarted = callback => {
|
||||
let started = false;
|
||||
const onPlayStarted = () => {
|
||||
if (!started) {
|
||||
started = true;
|
||||
callback();
|
||||
}
|
||||
};
|
||||
player.one("playing", onPlayStarted);
|
||||
};
|
||||
player.subscribeToManifestLoaded = callback => {
|
||||
if (this.hlsRef) {
|
||||
this.hlsRef.once(Hls.Events.MANIFEST_PARSED, () => callback());
|
||||
return;
|
||||
}
|
||||
player.one("loadedmetadata", () => callback());
|
||||
};
|
||||
}
|
||||
attachDurationAndPlayStartHooks() {
|
||||
const player = this.playerRef;
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
player.one("playing", () => {
|
||||
this.emit("playstart", {
|
||||
engine: this.currentEngine,
|
||||
});
|
||||
});
|
||||
player.one("loadedmetadata", () => {
|
||||
const duration = player.duration();
|
||||
if (typeof duration === "number" && !Number.isNaN(duration)) {
|
||||
this.emit("duration", { duration });
|
||||
}
|
||||
});
|
||||
}
|
||||
async loadCurrentSource(previousEngine) {
|
||||
if (!this.options || !this.playerRef || !this.videoRef) {
|
||||
return this.getState();
|
||||
}
|
||||
const nextEngine = this.resolveEngine(this.options);
|
||||
const source = this.options.source;
|
||||
if (previousEngine !== nextEngine || this.currentEngine !== nextEngine) {
|
||||
this.emit("enginechange", {
|
||||
previous: this.currentEngine,
|
||||
next: nextEngine,
|
||||
source,
|
||||
});
|
||||
}
|
||||
this.currentEngine = nextEngine;
|
||||
this.currentSource = source;
|
||||
if (nextEngine === "hls") {
|
||||
await this.loadHlsSource();
|
||||
}
|
||||
else {
|
||||
await this.loadVideoJsSource();
|
||||
}
|
||||
this.attachDurationAndPlayStartHooks();
|
||||
return this.getState();
|
||||
}
|
||||
buildHlsConfig(overrides = {}) {
|
||||
const options = this.options;
|
||||
if (!options) {
|
||||
return overrides;
|
||||
}
|
||||
const preferHqSettings = options.preferHQ
|
||||
? { abrEwmaDefaultEstimate: 10690560 * 1.2 }
|
||||
: {};
|
||||
const playlistLoader = createAuthPlaylistLoader({ debug: options.debug });
|
||||
return {
|
||||
debug: options.debug,
|
||||
enableWorker: true,
|
||||
fragLoadingMaxRetry: 2,
|
||||
manifestLoadingMaxRetry: 2,
|
||||
fragLoadingRetryDelay: 2000,
|
||||
manifestLoadingRetryDelay: 2000,
|
||||
forceKeyFrameOnDiscontinuity: true,
|
||||
backBufferLength: 90,
|
||||
appendErrorMaxRetry: 3,
|
||||
startPosition: options.initialTime > 0 ? options.initialTime : -1,
|
||||
testBandwidth: false,
|
||||
lowLatencyMode: false,
|
||||
liveSyncDurationCount: 2,
|
||||
maxBufferHole: 10,
|
||||
nudgeOffset: 0.1,
|
||||
nudgeMaxRetry: 5,
|
||||
highBufferWatchdogPeriod: 2,
|
||||
...preferHqSettings,
|
||||
...overrides,
|
||||
pLoader: playlistLoader,
|
||||
};
|
||||
}
|
||||
async loadHlsSource() {
|
||||
const options = this.options;
|
||||
const player = this.playerRef;
|
||||
const video = this.videoRef;
|
||||
if (!options || !player || !video) {
|
||||
return;
|
||||
}
|
||||
this.resetDeferredHlsLoading();
|
||||
this.teardownHls();
|
||||
player.pause();
|
||||
this.hlsLoaded = false;
|
||||
if (!Hls.isSupported()) {
|
||||
await this.loadVideoJsSource();
|
||||
return;
|
||||
}
|
||||
const setupHls = () => {
|
||||
if (this.hlsLoaded) {
|
||||
return;
|
||||
}
|
||||
this.hlsLoaded = true;
|
||||
const hls = new Hls(this.buildHlsConfig());
|
||||
this.hlsRef = hls;
|
||||
player.hlsInstance = hls;
|
||||
let recoveryAttempts = 0;
|
||||
const MAX_RECOVERY_ATTEMPTS = 10;
|
||||
let lastErrorTime = 0;
|
||||
const ERROR_RESET_TIME = 10000;
|
||||
let lastSegmentIndex = -1;
|
||||
const isAtLiveEdge = () => {
|
||||
if (!hls.liveSyncPosition || !video.duration) {
|
||||
return false;
|
||||
}
|
||||
return hls.liveSyncPosition - video.currentTime < 10;
|
||||
};
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, (_event, data) => {
|
||||
const details = data.levels?.[0]?.details;
|
||||
this.emit("manifestloaded", {
|
||||
engine: "hls",
|
||||
duration: details?.totalduration,
|
||||
live: details?.live,
|
||||
});
|
||||
if (details?.live) {
|
||||
video.parentElement?.classList.add("vjs-hls-live", "vjs-live");
|
||||
player.duration(Infinity);
|
||||
if (player.liveTracker) {
|
||||
player.liveTracker.isLive_ = true;
|
||||
player.liveTracker.startTracking();
|
||||
player.liveTracker.trigger("durationchange");
|
||||
}
|
||||
}
|
||||
if (options.initialTime > 0) {
|
||||
hls.startLoad(options.initialTime);
|
||||
}
|
||||
});
|
||||
hls.on(Hls.Events.FRAG_CHANGED, () => {
|
||||
if (player.liveTracker) {
|
||||
player.liveTracker.atLiveEdge = isAtLiveEdge;
|
||||
player.liveTracker.trigger("liveedgechange");
|
||||
}
|
||||
});
|
||||
hls.on(Hls.Events.ERROR, (_event, data) => {
|
||||
this.emit("error", {
|
||||
scope: "hls",
|
||||
error: data,
|
||||
fatal: data.fatal,
|
||||
});
|
||||
if (!data.fatal) {
|
||||
return;
|
||||
}
|
||||
const now = Date.now();
|
||||
if (now - lastErrorTime > ERROR_RESET_TIME) {
|
||||
recoveryAttempts = 0;
|
||||
lastSegmentIndex = -1;
|
||||
}
|
||||
lastErrorTime = now;
|
||||
if (recoveryAttempts >= MAX_RECOVERY_ATTEMPTS) {
|
||||
return;
|
||||
}
|
||||
recoveryAttempts += 1;
|
||||
switch (data.type) {
|
||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||
setTimeout(() => hls.startLoad(), 1000);
|
||||
break;
|
||||
case Hls.ErrorTypes.MEDIA_ERROR: {
|
||||
const currentLevel = hls.currentLevel;
|
||||
const details = hls.levels[currentLevel]?.details;
|
||||
if (details?.fragments?.length) {
|
||||
if (lastSegmentIndex === -1) {
|
||||
lastSegmentIndex = details.startSN;
|
||||
}
|
||||
lastSegmentIndex += 1;
|
||||
if (lastSegmentIndex < details.endSN) {
|
||||
const fragment = details.fragments[lastSegmentIndex - details.startSN];
|
||||
if (fragment) {
|
||||
const savedTime = fragment.start;
|
||||
video.currentTime = savedTime;
|
||||
setTimeout(() => {
|
||||
if (Math.abs(video.currentTime - savedTime) > 0.1) {
|
||||
video.currentTime = savedTime;
|
||||
}
|
||||
hls.recoverMediaError();
|
||||
if (!video.paused && options.autoplay) {
|
||||
void video.play().catch(() => undefined);
|
||||
}
|
||||
}, 100);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
recoveryAttempts = MAX_RECOVERY_ATTEMPTS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
setTimeout(() => hls.recoverMediaError(), 1000);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
setTimeout(() => {
|
||||
this.teardownHls();
|
||||
this.hlsLoaded = false;
|
||||
setupHls();
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
hls.loadSource(options.source.src);
|
||||
hls.attachMedia(video);
|
||||
if (options.autoplay && options.preload !== "none") {
|
||||
this.tryPlay(player);
|
||||
}
|
||||
};
|
||||
switch (options.preload) {
|
||||
case "none":
|
||||
this.originalPlayRef = player.play.bind(player);
|
||||
player.play = (...args) => {
|
||||
setupHls();
|
||||
return this.originalPlayRef(...args);
|
||||
};
|
||||
break;
|
||||
case "metadata": {
|
||||
const hls = new Hls(this.buildHlsConfig({
|
||||
autoStartLoad: false,
|
||||
}));
|
||||
this.hlsLoaded = true;
|
||||
this.hlsRef = hls;
|
||||
player.hlsInstance = hls;
|
||||
hls.attachMedia(video);
|
||||
hls.loadSource(options.source.src);
|
||||
break;
|
||||
}
|
||||
case "visibility":
|
||||
if (typeof IntersectionObserver !== "undefined" && this.containerRef) {
|
||||
this.visibilityObserverRef = new IntersectionObserver(entries => {
|
||||
if (entries[0]?.isIntersecting) {
|
||||
setupHls();
|
||||
this.visibilityObserverRef?.disconnect();
|
||||
this.visibilityObserverRef = null;
|
||||
}
|
||||
}, { threshold: 0.25 });
|
||||
this.visibilityObserverRef.observe(this.containerRef);
|
||||
}
|
||||
else {
|
||||
setupHls();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
setupHls();
|
||||
}
|
||||
}
|
||||
async loadVideoJsSource() {
|
||||
const options = this.options;
|
||||
const player = this.playerRef;
|
||||
if (!options || !player) {
|
||||
return;
|
||||
}
|
||||
player.pause();
|
||||
this.resetDeferredHlsLoading();
|
||||
this.teardownHls();
|
||||
try {
|
||||
const token = await resolveVideoPlayerToken();
|
||||
this.vhsAuthTokenRef = token;
|
||||
this.ensureVhsAuthInterceptor(player);
|
||||
}
|
||||
catch (error) {
|
||||
this.emit("error", {
|
||||
scope: "runtime",
|
||||
error,
|
||||
});
|
||||
}
|
||||
player.src([
|
||||
{
|
||||
src: options.source.src,
|
||||
type: options.source.type ?? DEFAULT_SOURCE_TYPE,
|
||||
},
|
||||
]);
|
||||
if (options.initialTime > 0) {
|
||||
player.one("loadedmetadata", () => {
|
||||
player.currentTime(options.initialTime);
|
||||
});
|
||||
}
|
||||
if (options.autoplay) {
|
||||
this.tryPlay(player);
|
||||
}
|
||||
}
|
||||
resetDeferredHlsLoading() {
|
||||
if (this.visibilityObserverRef) {
|
||||
this.visibilityObserverRef.disconnect();
|
||||
this.visibilityObserverRef = null;
|
||||
}
|
||||
if (this.originalPlayRef && this.playerRef) {
|
||||
this.playerRef.play = this.originalPlayRef;
|
||||
this.originalPlayRef = null;
|
||||
}
|
||||
}
|
||||
ensureVhsAuthInterceptor(player) {
|
||||
if (this.vhsRequestCleanupRef) {
|
||||
return;
|
||||
}
|
||||
const videojsAny = videojs;
|
||||
const xhr = videojsAny?.Vhs?.xhr ?? videojsAny?.Hls?.xhr;
|
||||
if (!xhr) {
|
||||
return;
|
||||
}
|
||||
const originalBeforeRequest = xhr.beforeRequest;
|
||||
xhr.beforeRequest = (requestOptions) => {
|
||||
const processedOptions = originalBeforeRequest?.call(xhr, requestOptions) ?? requestOptions;
|
||||
if (this.vhsAuthTokenRef) {
|
||||
processedOptions.headers = {
|
||||
...(processedOptions.headers ?? {}),
|
||||
Authorization: `Bearer ${this.vhsAuthTokenRef}`,
|
||||
};
|
||||
}
|
||||
return processedOptions;
|
||||
};
|
||||
this.vhsRequestCleanupRef = () => {
|
||||
xhr.beforeRequest = originalBeforeRequest;
|
||||
};
|
||||
player.one("dispose", () => {
|
||||
this.vhsRequestCleanupRef?.();
|
||||
this.vhsRequestCleanupRef = null;
|
||||
});
|
||||
}
|
||||
teardownHls() {
|
||||
if (!this.hlsRef) {
|
||||
return;
|
||||
}
|
||||
this.hlsRef.stopLoad();
|
||||
this.hlsRef.detachMedia();
|
||||
this.hlsRef.destroy();
|
||||
this.hlsRef = null;
|
||||
this.hlsLoaded = false;
|
||||
if (this.playerRef) {
|
||||
this.playerRef.hlsInstance = null;
|
||||
const videoElement = this.playerRef
|
||||
.el()
|
||||
?.querySelector("video");
|
||||
videoElement?.parentElement?.classList.remove("vjs-hls-live", "vjs-live");
|
||||
if (this.playerRef.liveTracker) {
|
||||
this.playerRef.liveTracker.isLive_ = false;
|
||||
this.playerRef.liveTracker.trigger("durationchange");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=player-runtime.js.map
|
||||
1
packages/video-player/dist/core/player-runtime.js.map
vendored
Normal file
1
packages/video-player/dist/core/player-runtime.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user