Compare commits
2 Commits
v0.0.1
...
video-play
| Author | SHA1 | Date | |
|---|---|---|---|
| 028ce21c4c | |||
| 915c56351b |
13
.gitignore
vendored
13
.gitignore
vendored
@@ -1,3 +1,12 @@
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
node_modules
|
||||
*.log
|
||||
storybook-static
|
||||
**/node_modules
|
||||
**/.turbo
|
||||
coverage
|
||||
.DS_Store
|
||||
**/storybook-static
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
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",
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -1,25 +0,0 @@
|
||||
:root {
|
||||
--Default-BgLight: #ffffff;
|
||||
--Default-BgDarken: #f2f2f2;
|
||||
--Default-StrokeDividers: #e0e0e0;
|
||||
--Default-White: #ffffff;
|
||||
--Text-Primary: #1d1d1d;
|
||||
--Controls-Primary: #1d1d1d;
|
||||
--Controls-Plashes: #f5f5f5;
|
||||
--Shadow-Z100: 0 10px 24px rgba(20, 20, 20, 0.14);
|
||||
--Accent-Primary: #5152ba;
|
||||
--Opacity-BlackOpacity45: rgba(0, 0, 0, 0.45);
|
||||
--corner-S: 8px;
|
||||
--corner-M: 12px;
|
||||
--Corner-XL: 32px;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#storybook-root {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #f6f8fb;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import type { Preview } from "@storybook/react";
|
||||
|
||||
import "antd/dist/reset.css";
|
||||
import "../src/react/video-player/tach-video-js.css";
|
||||
import "./preview.css";
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
layout: "padded",
|
||||
controls: {
|
||||
expanded: true,
|
||||
sort: "requiredFirst",
|
||||
},
|
||||
options: {
|
||||
storySort: {
|
||||
order: ["React", "Angular"],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
||||
8
.yarnrc.yml
Normal file
8
.yarnrc.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
packageExtensions:
|
||||
ng-zorro-antd@*:
|
||||
peerDependencies:
|
||||
rxjs: ">=7.0.0"
|
||||
215
README.md
215
README.md
@@ -1,193 +1,60 @@
|
||||
# @tach/video-player
|
||||
# hublib-web
|
||||
|
||||
Single package with three entrypoints:
|
||||
Monorepo for shared UI packages distributed with Yarn workspaces.
|
||||
|
||||
- `@tach/video-player/core`
|
||||
- `@tach/video-player/react`
|
||||
- `@tach/video-player/angular`
|
||||
External projects install these packages directly from Git tags. Package `dist/` artifacts must be committed.
|
||||
|
||||
This folder is prepared to be moved into a dedicated repository as-is.
|
||||
## Structure
|
||||
|
||||
## What Is Included
|
||||
- `packages/tach-typography` - typography tokens and adapters for React/Angular.
|
||||
- `packages/video-player` - cross-framework video player runtime and adapters.
|
||||
- `packages/content-suggestions` - content text/title with mention/tag/link parsing for React/Angular.
|
||||
|
||||
- Build pipeline to `dist/`:
|
||||
- `npm run build`
|
||||
- `npm run typecheck`
|
||||
- Storybook playground for framework entrypoints:
|
||||
- `npm run storybook`
|
||||
- `npm run build-storybook`
|
||||
- `dist`-first flow for git-based installation:
|
||||
- `dist/` is committed to the repository.
|
||||
- consumers do not depend on `prepare` at install time.
|
||||
- Version bump scripts:
|
||||
- `npm run release:patch`
|
||||
- `npm run release:minor`
|
||||
- `npm run release:major`
|
||||
- Optional framework peers:
|
||||
- React peers are optional if only `core/angular` is used.
|
||||
- Angular peers are optional if only `core/react` is used.
|
||||
## Package READMEs
|
||||
|
||||
## Repo Transfer Checklist
|
||||
- [tach-typography](./packages/tach-typography/README.md)
|
||||
- [video-player](./packages/video-player/README.md)
|
||||
- [content-suggestions](./packages/content-suggestions/README.md)
|
||||
|
||||
1. Create a new repository (for example `tach/video-player`).
|
||||
2. Copy everything from this folder to the new repo root.
|
||||
3. In the new repo run:
|
||||
## Development
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
corepack enable
|
||||
yarn set version 4.12.0
|
||||
yarn install
|
||||
yarn build
|
||||
yarn test
|
||||
```
|
||||
|
||||
4. Commit and push (including `dist/`):
|
||||
## Add a new package
|
||||
|
||||
1. Create a folder inside `packages/<name>`.
|
||||
2. Add `package.json` with a unique package name.
|
||||
3. Link internal dependencies with `workspace:*` (or `workspace:^`).
|
||||
4. Run `yarn install` and then `yarn build`.
|
||||
|
||||
## Installation from Git (SSH)
|
||||
|
||||
This repository is consumed by tag-based Git dependencies.
|
||||
|
||||
- Full install guide: [docs/git-installation.md](./docs/git-installation.md)
|
||||
- Release workflow: [docs/release-policy.md](./docs/release-policy.md)
|
||||
|
||||
Command template:
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "feat: initial @tach/video-player package"
|
||||
git push origin main
|
||||
yarn add "@hublib-web/<package>@git+ssh://git@github.com/ORG/REPO.git#workspace=@hublib-web/<package>&tag=<package>-vX.Y.Z"
|
||||
```
|
||||
|
||||
5. Create a release tag:
|
||||
## Releases
|
||||
|
||||
```bash
|
||||
git tag v0.1.0
|
||||
git push origin v0.1.0
|
||||
```
|
||||
Releases are done with Git tags (no Changesets, no npm publish).
|
||||
|
||||
## Build Output
|
||||
Short flow:
|
||||
|
||||
`npm run build` compiles TypeScript into `dist/` and copies required assets (`css/scss/svg/...`).
|
||||
`dist/` must be committed before creating a new version tag.
|
||||
1. Bump version in `packages/<package>/package.json`.
|
||||
2. Build package and commit updated `dist/` artifacts.
|
||||
3. Create an annotated package tag: `<package>-vX.Y.Z`.
|
||||
4. Push commit and tags to origin.
|
||||
|
||||
Entrypoints are exported from `dist`:
|
||||
|
||||
- `@tach/video-player/core`
|
||||
- `@tach/video-player/react`
|
||||
- `@tach/video-player/angular`
|
||||
|
||||
## Storybook
|
||||
|
||||
Local Storybook exposes two pages for development and prop testing:
|
||||
|
||||
- `React/VideoPlayer` - React component entrypoint with controls for player props.
|
||||
- `Angular/VideoPlayerAdapter` - adapter entrypoint with controls for runtime options (`attach/update`).
|
||||
|
||||
Run Storybook:
|
||||
|
||||
```bash
|
||||
npm run storybook
|
||||
```
|
||||
|
||||
Build static Storybook:
|
||||
|
||||
```bash
|
||||
npm run build-storybook
|
||||
```
|
||||
|
||||
## Installation From Git (Without npm Registry)
|
||||
|
||||
### npm
|
||||
|
||||
By tag:
|
||||
|
||||
```bash
|
||||
npm i git+ssh://git@github.com/<org>/video-player.git#v0.1.0
|
||||
```
|
||||
|
||||
By commit:
|
||||
|
||||
```bash
|
||||
npm i git+ssh://git@github.com/<org>/video-player.git#<commit-sha>
|
||||
```
|
||||
|
||||
By semver tag range:
|
||||
|
||||
```bash
|
||||
npm i github:<org>/video-player#semver:^0.1.0
|
||||
```
|
||||
|
||||
### pnpm
|
||||
|
||||
```bash
|
||||
pnpm add git+ssh://git@github.com/<org>/video-player.git#v0.1.0
|
||||
```
|
||||
|
||||
### yarn
|
||||
|
||||
```bash
|
||||
yarn add git+ssh://git@github.com/<org>/video-player.git#v0.1.0
|
||||
```
|
||||
|
||||
## Versioning Workflow
|
||||
|
||||
1. Build and bump version:
|
||||
|
||||
```bash
|
||||
npm run release:patch
|
||||
# or release:minor / release:major
|
||||
```
|
||||
|
||||
2. Commit generated `dist` changes and `package.json` version bump:
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "chore(release): vX.Y.Z"
|
||||
```
|
||||
|
||||
3. Push commit and tags:
|
||||
|
||||
```bash
|
||||
git push origin main --follow-tags
|
||||
```
|
||||
|
||||
4. Consumers update git tag/version in their `package.json`.
|
||||
|
||||
## Why Optional Peers
|
||||
|
||||
Consumers install only the framework they need:
|
||||
|
||||
- React app: install `react` and `react-dom`, use `@tach/video-player/react`.
|
||||
- Angular app: install `@angular/*`, use `@tach/video-player/angular`.
|
||||
- Shared utilities only: use `@tach/video-player/core`.
|
||||
|
||||
## Entrypoints
|
||||
|
||||
### Core
|
||||
|
||||
```ts
|
||||
import {
|
||||
isHlsSource,
|
||||
selectPlaybackEngine,
|
||||
VideoPlayerRuntime,
|
||||
setVideoPlayerTokenProvider,
|
||||
} from "@tach/video-player/core";
|
||||
|
||||
setVideoPlayerTokenProvider(async () => {
|
||||
// Provide host-app token retrieval here.
|
||||
return null;
|
||||
});
|
||||
|
||||
const runtime = new VideoPlayerRuntime();
|
||||
await runtime.init({
|
||||
container: document.getElementById("player")!,
|
||||
source: { src: "https://example.com/video.m3u8" },
|
||||
});
|
||||
```
|
||||
|
||||
### React
|
||||
|
||||
```tsx
|
||||
import VideoPlayer from "@tach/video-player/react";
|
||||
```
|
||||
|
||||
### Angular
|
||||
|
||||
```ts
|
||||
import { AngularVideoPlayerAdapter } from "@tach/video-player/angular";
|
||||
|
||||
const adapter = new AngularVideoPlayerAdapter();
|
||||
await adapter.attach(containerElement, {
|
||||
source: { src: "https://example.com/video.m3u8" },
|
||||
});
|
||||
```
|
||||
|
||||
`AngularVideoPlayerAdapter` is intentionally framework-light: it wraps `VideoPlayerRuntime` (`attach/update/destroy/on`) and does not depend on React code.
|
||||
Detailed policy and examples: [docs/release-policy.md](./docs/release-policy.md).
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import React from "react";
|
||||
import Player from "video.js/dist/types/player";
|
||||
interface VideoQRScannerPluginProps {
|
||||
player: Player | null;
|
||||
enabled?: boolean;
|
||||
/**
|
||||
* Интервал между запусками сканирования в миллисекундах.
|
||||
* По умолчанию 200 мс.
|
||||
*/
|
||||
scanInterval?: number;
|
||||
/**
|
||||
* Масштаб для сканирования (0 < scanningScale <= 1).
|
||||
* Значение меньше 1 уменьшает разрешение для ускорения обработки.
|
||||
* По умолчанию 1.
|
||||
*/
|
||||
scanningScale?: number;
|
||||
/**
|
||||
* Количество неудачных попыток обнаружения QR-кода до снятия маски.
|
||||
* По умолчанию 2.a
|
||||
*/
|
||||
maxFailedAttempts?: number;
|
||||
}
|
||||
declare const VideoQRScannerPlugin: React.FC<VideoQRScannerPluginProps>;
|
||||
export default VideoQRScannerPlugin;
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/react/video-player/components/qr-scanner/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAKxE,OAAO,MAAM,MAAM,4BAA4B,CAAC;AAIhD,UAAU,yBAAyB;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,QAAA,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAqR7D,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
||||
@@ -1,209 +0,0 @@
|
||||
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { Popover } from "antd";
|
||||
import QrScanner from "qr-scanner";
|
||||
import { CloseOutlined } from "@ant-design/icons";
|
||||
const VideoQRScannerPlugin = ({ player, enabled = true, scanInterval = 200, scanningScale = 1, maxFailedAttempts = 2, }) => {
|
||||
const canvasRef = useRef(null);
|
||||
const [detectedQr, setDetectedQr] = useState(null);
|
||||
const animationFrameRef = useRef(null);
|
||||
const ignoredQRCodes = useRef(new Set());
|
||||
const [playerEl, setPlayerEl] = useState(null);
|
||||
const lastScanTimeRef = useRef(0);
|
||||
const isScanningRef = useRef(false);
|
||||
// Счётчик неудачных попыток обнаружения QR, если код уже отображается
|
||||
const failedAttemptsRef = useRef(0);
|
||||
// Для получения актуального значения detectedQr внутри асинхронного колбэка
|
||||
const detectedQrRef = useRef(null);
|
||||
useEffect(() => {
|
||||
detectedQrRef.current = detectedQr;
|
||||
}, [detectedQr]);
|
||||
// Изначально устанавливаем контейнер для портала как элемент плеера
|
||||
useEffect(() => {
|
||||
if (player) {
|
||||
setPlayerEl(player.el());
|
||||
}
|
||||
}, [player]);
|
||||
// Обновление контейнера при переходе в полноэкранный режим
|
||||
useEffect(() => {
|
||||
const handleFullscreenChange = () => {
|
||||
if (document.fullscreenElement) {
|
||||
setPlayerEl(document.fullscreenElement);
|
||||
}
|
||||
else if (player) {
|
||||
setPlayerEl(player.el());
|
||||
}
|
||||
};
|
||||
document.addEventListener("fullscreenchange", handleFullscreenChange);
|
||||
return () => {
|
||||
document.removeEventListener("fullscreenchange", handleFullscreenChange);
|
||||
};
|
||||
}, [player]);
|
||||
const scanFrame = useCallback(async () => {
|
||||
if (!player || player.paused() || !canvasRef.current || !enabled)
|
||||
return;
|
||||
const now = performance.now();
|
||||
if (now - lastScanTimeRef.current < scanInterval) {
|
||||
animationFrameRef.current = requestAnimationFrame(scanFrame);
|
||||
return;
|
||||
}
|
||||
lastScanTimeRef.current = now;
|
||||
// Предотвращаем параллельное выполнение сканирования
|
||||
if (isScanningRef.current) {
|
||||
animationFrameRef.current = requestAnimationFrame(scanFrame);
|
||||
return;
|
||||
}
|
||||
isScanningRef.current = true;
|
||||
const videoEl = player.tech(true).el();
|
||||
const canvas = canvasRef.current;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) {
|
||||
isScanningRef.current = false;
|
||||
return;
|
||||
}
|
||||
// Получаем оригинальные размеры видео
|
||||
const originalVideoWidth = videoEl.videoWidth;
|
||||
const originalVideoHeight = videoEl.videoHeight;
|
||||
// Устанавливаем canvas с пониженным разрешением для сканирования
|
||||
canvas.width = originalVideoWidth * scanningScale;
|
||||
canvas.height = originalVideoHeight * scanningScale;
|
||||
// Рисуем видео в canvas с пониженным разрешением
|
||||
ctx.drawImage(videoEl, 0, 0, canvas.width, canvas.height);
|
||||
try {
|
||||
const result = await QrScanner.scanImage(canvas, {
|
||||
returnDetailedScanResult: true,
|
||||
});
|
||||
if (result &&
|
||||
result.data &&
|
||||
result.cornerPoints?.length === 4 &&
|
||||
!ignoredQRCodes.current.has(result.data)) {
|
||||
// Сброс неудачных попыток при успешном обнаружении
|
||||
failedAttemptsRef.current = 0;
|
||||
// Преобразуем координаты из масштабированного canvas в координаты оригинального видео
|
||||
const points = result.cornerPoints.map(p => ({
|
||||
x: p.x / scanningScale,
|
||||
y: p.y / scanningScale,
|
||||
}));
|
||||
const minX = Math.min(...points.map(p => p.x));
|
||||
const minY = Math.min(...points.map(p => p.y));
|
||||
const maxX = Math.max(...points.map(p => p.x));
|
||||
const maxY = Math.max(...points.map(p => p.y));
|
||||
// Получаем размеры отображаемого видео
|
||||
const rect = videoEl.getBoundingClientRect();
|
||||
const displayScale = Math.min(rect.width / originalVideoWidth, rect.height / originalVideoHeight);
|
||||
const offsetX = (rect.width - originalVideoWidth * displayScale) / 2;
|
||||
const offsetY = (rect.height - originalVideoHeight * displayScale) / 2;
|
||||
const qrWidth = (maxX - minX) * displayScale;
|
||||
const qrHeight = (maxY - minY) * displayScale;
|
||||
const padding = 8;
|
||||
const x = minX * displayScale + offsetX - padding;
|
||||
const y = minY * displayScale + offsetY - padding;
|
||||
setDetectedQr({
|
||||
data: result.data,
|
||||
position: new DOMRect(x, y, qrWidth + padding * 2, qrHeight + padding * 2),
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Если код не найден, и он уже отображался, даем maxFailedAttempts попыток
|
||||
if (detectedQrRef.current) {
|
||||
failedAttemptsRef.current += 1;
|
||||
if (failedAttemptsRef.current >= maxFailedAttempts) {
|
||||
setDetectedQr(null);
|
||||
failedAttemptsRef.current = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// В случае ошибки аналогичная логика: если код уже отображался — даем maxFailedAttempts попыток
|
||||
if (detectedQrRef.current) {
|
||||
failedAttemptsRef.current += 1;
|
||||
if (failedAttemptsRef.current >= maxFailedAttempts) {
|
||||
setDetectedQr(null);
|
||||
failedAttemptsRef.current = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
isScanningRef.current = false;
|
||||
}
|
||||
animationFrameRef.current = requestAnimationFrame(scanFrame);
|
||||
}, [player, enabled, scanInterval, scanningScale, maxFailedAttempts]);
|
||||
useEffect(() => {
|
||||
if (!player || !enabled)
|
||||
return;
|
||||
const startScanning = () => {
|
||||
if (animationFrameRef.current)
|
||||
cancelAnimationFrame(animationFrameRef.current);
|
||||
animationFrameRef.current = requestAnimationFrame(scanFrame);
|
||||
};
|
||||
const stopScanning = () => {
|
||||
if (animationFrameRef.current) {
|
||||
cancelAnimationFrame(animationFrameRef.current);
|
||||
animationFrameRef.current = null;
|
||||
}
|
||||
setDetectedQr(null);
|
||||
};
|
||||
player.on("play", startScanning);
|
||||
player.on("pause", stopScanning);
|
||||
player.on("ended", stopScanning);
|
||||
window.addEventListener("resize", startScanning);
|
||||
return () => {
|
||||
stopScanning();
|
||||
player.off("play", startScanning);
|
||||
player.off("pause", stopScanning);
|
||||
player.off("ended", stopScanning);
|
||||
window.removeEventListener("resize", startScanning);
|
||||
};
|
||||
}, [player, enabled, scanFrame]);
|
||||
const ignoreQRCode = () => {
|
||||
if (detectedQr) {
|
||||
ignoredQRCodes.current.add(detectedQr.data);
|
||||
setDetectedQr(null);
|
||||
}
|
||||
};
|
||||
return (_jsxs(_Fragment, { children: [_jsx("canvas", { ref: canvasRef, style: { display: "none" } }), detectedQr &&
|
||||
playerEl &&
|
||||
createPortal(_jsx(Popover, { getPopupContainer: () => playerEl, content: _jsx("a", { href: detectedQr.data, target: "_blank", rel: "noreferrer", referrerPolicy: "no-referrer", style: { color: "#1677ff" }, children: detectedQr.data }), placement: "top", children: _jsxs("div", { style: {
|
||||
position: "absolute",
|
||||
left: detectedQr.position.x,
|
||||
top: detectedQr.position.y,
|
||||
width: detectedQr.position.width,
|
||||
height: detectedQr.position.height,
|
||||
pointerEvents: "auto",
|
||||
cursor: "pointer",
|
||||
zIndex: 10,
|
||||
}, children: [["top-left", "top-right", "bottom-left", "bottom-right"].map(corner => (_jsx("div", { style: {
|
||||
position: "absolute",
|
||||
width: 20,
|
||||
height: 20,
|
||||
border: "3px solid var(--Accent-Primary)",
|
||||
borderRadius: 4,
|
||||
...(corner.includes("top") ? { top: 0 } : { bottom: 0 }),
|
||||
...(corner.includes("left") ? { left: 0 } : { right: 0 }),
|
||||
borderTop: corner.includes("bottom")
|
||||
? "none"
|
||||
: "3px solid var(--Accent-Primary)",
|
||||
borderBottom: corner.includes("top")
|
||||
? "none"
|
||||
: "3px solid var(--Accent-Primary)",
|
||||
borderLeft: corner.includes("right")
|
||||
? "none"
|
||||
: "3px solid var(--Accent-Primary)",
|
||||
borderRight: corner.includes("left")
|
||||
? "none"
|
||||
: "3px solid var(--Accent-Primary)",
|
||||
} }, corner))), _jsx(CloseOutlined, { style: {
|
||||
color: "#ff4d4f",
|
||||
position: "absolute",
|
||||
top: "-8px",
|
||||
right: "-8px",
|
||||
background: "white",
|
||||
borderRadius: "50%",
|
||||
padding: "2px",
|
||||
cursor: "pointer",
|
||||
}, onClick: ignoreQRCode })] }) }), playerEl)] }));
|
||||
};
|
||||
export default VideoQRScannerPlugin;
|
||||
//# sourceMappingURL=index.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/react/video-player/components/video-js/plugins/settings/index.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,MAAM,+BAA+B,CAAC;AAEtD,OAAO,MAAM,MAAM,4BAA4B,CAAC;AAYhD,OAAO,gBAAgB,CAAC;AAExB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,UAAU,qBAAqB;CAE9B;AAGD,UAAU,oBAAqB,SAAQ,aAAa;IACnD,UAAU,EAAE,SAAS,CAAC;CACtB;AAED,QAAA,MAAM,UAAU,EAAmC,OAAO,MAAM,CAAC;AAKjE,cAAM,cAAe,SAAQ,UAAU;IACtC,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,cAAc,CAAa;IAGnC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,gBAAgB,CAA6B;gBAEzC,MAAM,EAAE,oBAAoB,EAAE,OAAO,EAAE,qBAAqB;IAMxE;;OAEG;IACH,OAAO,CAAC,UAAU;IAKlB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAKxB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IA8D5B;;;;;OAKG;IACH,OAAO,CAAC,WAAW;IAInB;;;;;;OAMG;YACW,OAAO;CA8CrB;AAID,eAAe,cAAc,CAAC"}
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/react/video-player/components/video-js/plugins/settings/selectors/text-track-selector/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAS3D,UAAU,kBAAmB,SAAQ,yBAAyB;CAAG;AAEjE,QAAA,MAAM,UAAU,GACf,QAAQ,aAAa,EACrB,UAAS,kBAA6B;;;;;;;;;;;;;;;CAgDtC,CAAC;AAEF,eAAe,UAAU,CAAC"}
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../../../src/react/video-player/components/video-js/plugins/settings/selectors/text-track-selector/index.ts"],"names":[],"mappings":"AAGA,MAAM,QAAQ,GAAG;IAChB,KAAK,EAAE,UAAU;IACjB,QAAQ,EAAE,KAAK;IACf,UAAU,EAAE,KAAK;IACjB,SAAS,EAAE,eAAe;CAC1B,CAAC;AAIF,MAAM,UAAU,GAAG,CAClB,MAAqB,EACrB,UAA8B,QAAQ,EACrC,EAAE;IACH,MAAM,SAAS,GAAG,GAAG,EAAE;QACtB,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC;QAC/C,MAAM,UAAU,GAAG,EAAE,CAAC;QAEtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC;YAC7C,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrC,SAAS;YACV,CAAC;YAED,MAAM,SAAS,GAAG;gBACjB,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK;gBAC1B,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK;gBAC1B,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS;gBAC1C,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;aACtC,CAAC;YAEF,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,UAAU,CAAC;IACnB,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,CAAC,KAAgB,EAAE,EAAE;QACrC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC;QAE/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC;YAC7C,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrC,SAAS;YACV,CAAC;YAED,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK;gBAAE,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,SAAS,CAAC;;gBACnE,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,UAAU,CAAC;QACtC,CAAC;IACF,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,GAAG,EAAE;QACrB,OAAO;YACN,GAAG,OAAO;YACV,OAAO,EAAE,SAAS,EAAE,CAAC,MAAM,GAAG,CAAC;SAC/B,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACN,SAAS;QACT,QAAQ;KACR,CAAC;AACH,CAAC,CAAC;AAEF,eAAe,UAAU,CAAC"}
|
||||
@@ -1,51 +0,0 @@
|
||||
import Player from "video.js/dist/types/player";
|
||||
import { SkipButtonsOptions } from "./plugins/skip-buttons";
|
||||
export interface IVideoJSSource {
|
||||
src: string;
|
||||
type: string;
|
||||
}
|
||||
export type PreloadType = "auto" | "metadata" | "none" | "visibility";
|
||||
export interface IVideoJSOptions {
|
||||
autoplay: boolean;
|
||||
controls: boolean;
|
||||
responsive: boolean;
|
||||
aspectRatio?: string;
|
||||
preload: PreloadType;
|
||||
fluid: boolean;
|
||||
muted: boolean;
|
||||
sources: IVideoJSSource[];
|
||||
poster?: string;
|
||||
preferHQ?: boolean;
|
||||
/** Включить детальное логирование */
|
||||
debug?: boolean;
|
||||
}
|
||||
export interface IVideoJSProps {
|
||||
options: IVideoJSOptions;
|
||||
onReady?: (player: VideoJsPlayer) => void;
|
||||
className?: string;
|
||||
classNames?: string[];
|
||||
initialTime?: number;
|
||||
full?: boolean;
|
||||
withRewind?: boolean;
|
||||
}
|
||||
export interface VideoJsLiveTracker {
|
||||
isLive_: boolean;
|
||||
atLiveEdge?: () => boolean;
|
||||
startTracking: () => void;
|
||||
trigger: (event: string) => void;
|
||||
}
|
||||
export type VideoJsPlayer = Player & {
|
||||
liveTracker?: VideoJsLiveTracker;
|
||||
settingsMenu?: () => void;
|
||||
mobileUi?: () => void;
|
||||
bigPlayPauseButton?: () => void;
|
||||
skipButtons?: (options: SkipButtonsOptions) => void;
|
||||
subscribeToSegmentChange: (callback: (segment: any) => void) => void;
|
||||
subscribeToDuration: (callback: (duration: number) => void) => void;
|
||||
subscribeToPlayStart: (callback: () => void) => void;
|
||||
subscribeToPlayStarted: (callback: () => void) => void;
|
||||
subscribeToManifestLoaded: (callback: () => void) => void;
|
||||
mediaduration: () => number | undefined;
|
||||
textTracks: () => TextTrack[];
|
||||
};
|
||||
//# sourceMappingURL=types.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/react/video-player/components/video-js/types.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,4BAA4B,CAAC;AAEhD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,YAAY,CAAC;AAEtE,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,eAAe,CAAC;IACzB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;IAC3B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAGD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG;IACpC,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACpD,wBAAwB,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,KAAK,IAAI,CAAC;IACrE,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;IACxC,UAAU,EAAE,MAAM,SAAS,EAAE,CAAC;CAC9B,CAAC"}
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/react/video-player/components/with-errors/index.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAMrD,OAAO,UAAU,MAAM,eAAe,CAAC;AACvC,OAAO,MAAM,MAAM,2BAA2B,CAAC;AAO/C,MAAM,UAAU,GAAG,CAAC,EACnB,UAAU,GAAG,KAAK,EAClB,QAAQ,EACR,GAAG,KAAK,EACqB,EAAE,EAAE;IAEjC,OAAO,CACN,eACC,SAAS,EAAE,MAAM,CAAC,SAAS,EAC3B,KAAK,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,aAEhD,UAAU,IAAI,KAAC,UAAU,IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,GAAI,EACtD,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,GAAG,KAAK,EAAE,CAAC,IACtC,CACN,CAAC;AACH,CAAC,CAAC;AAEF,eAAe,UAAU,CAAC"}
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/react/video-player/components/with-mouse-events/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAIjC,OAAO,MAAM,MAAM,4BAA4B,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAiB,MAAM,mBAAmB,CAAC;AAGjE,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC3D,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1C,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC/C,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC/C,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,eAAe,GAAI,sEAO7B,qBAAqB,4CAkEvB,CAAC"}
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/react/video-player/components/with-observation/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAKjC,OAAO,MAAM,MAAM,4BAA4B,CAAC;AAGhD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAG7D,MAAM,WAAW,qBAAsB,SAAQ,qBAAqB;IACnE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,eAAe,GAAI,iDAM7B,qBAAqB,4CAsDvB,CAAC"}
|
||||
43
docs/git-installation.md
Normal file
43
docs/git-installation.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Git installation (SSH + tags)
|
||||
|
||||
This repository is consumed directly from Git tags, not from npm.
|
||||
|
||||
## Requirements
|
||||
|
||||
- You have SSH access to the repository (`git@github.com:ORG/REPO.git`).
|
||||
- Release tags already exist in remote.
|
||||
- `dist/` is committed in the tagged commit.
|
||||
|
||||
## Tag naming
|
||||
|
||||
Use package-specific tags:
|
||||
|
||||
- `tach-typography-vX.Y.Z`
|
||||
- `video-player-vX.Y.Z`
|
||||
- `content-suggestions-vX.Y.Z`
|
||||
|
||||
## Yarn template
|
||||
|
||||
Always wrap the dependency string in quotes because of `&tag=`.
|
||||
|
||||
```bash
|
||||
yarn add "@hublib-web/<package>@git+ssh://git@github.com/ORG/REPO.git#workspace=@hublib-web/<package>&tag=<package>-vX.Y.Z"
|
||||
```
|
||||
|
||||
## Package examples
|
||||
|
||||
```bash
|
||||
yarn add "@hublib-web/tach-typography@git+ssh://git@github.com/ORG/REPO.git#workspace=@hublib-web/tach-typography&tag=tach-typography-v0.1.0"
|
||||
yarn add "@hublib-web/video-player@git+ssh://git@github.com/ORG/REPO.git#workspace=@hublib-web/video-player&tag=video-player-v0.1.0"
|
||||
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"
|
||||
```
|
||||
|
||||
## Update package version in a consumer app
|
||||
|
||||
```bash
|
||||
yarn up "@hublib-web/video-player@git+ssh://git@github.com/ORG/REPO.git#workspace=@hublib-web/video-player&tag=video-player-v0.2.0"
|
||||
```
|
||||
|
||||
## Related docs
|
||||
|
||||
- Release policy: [release-policy.md](./release-policy.md)
|
||||
54
docs/release-policy.md
Normal file
54
docs/release-policy.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Release policy
|
||||
|
||||
This monorepo is released through Git tags. We do not use Changesets or npm publish.
|
||||
|
||||
## Principles
|
||||
|
||||
- Every release points to a commit where the package `dist/` is already built and committed.
|
||||
- Tags are package-specific, so each package can be released independently.
|
||||
- Consumers install by tag using Git SSH URLs.
|
||||
|
||||
## Tag format
|
||||
|
||||
- `tach-typography-vX.Y.Z`
|
||||
- `video-player-vX.Y.Z`
|
||||
- `content-suggestions-vX.Y.Z`
|
||||
|
||||
`X.Y.Z` must match the version in the package's `package.json`.
|
||||
|
||||
## Release checklist (single package)
|
||||
|
||||
1. Update package version in `packages/<package>/package.json`.
|
||||
2. Build package:
|
||||
|
||||
```bash
|
||||
yarn workspace @hublib-web/<package> build
|
||||
```
|
||||
|
||||
3. Verify that `dist/` and `package.json` contain the release changes.
|
||||
4. Commit release files:
|
||||
|
||||
```bash
|
||||
git add packages/<package>/package.json packages/<package>/dist
|
||||
git commit -m "release(<package>): vX.Y.Z"
|
||||
```
|
||||
|
||||
5. Create annotated tag:
|
||||
|
||||
```bash
|
||||
git tag -a <package>-vX.Y.Z -m "@hublib-web/<package> vX.Y.Z"
|
||||
```
|
||||
|
||||
6. Push commit and tags:
|
||||
|
||||
```bash
|
||||
git push origin main --follow-tags
|
||||
```
|
||||
|
||||
## Multi-package release
|
||||
|
||||
If one commit releases several packages, create one tag per package on the same commit.
|
||||
|
||||
## Consumer installation
|
||||
|
||||
Use the package-specific tag in dependency spec. See [git-installation.md](./git-installation.md).
|
||||
18
eslint.config.mjs
Normal file
18
eslint.config.mjs
Normal file
@@ -0,0 +1,18 @@
|
||||
import js from "@eslint/js";
|
||||
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
{
|
||||
files: ["**/*.ts", "**/*.tsx"],
|
||||
languageOptions: {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
},
|
||||
rules: {
|
||||
"no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ["**/dist/**", "**/node_modules/**"],
|
||||
},
|
||||
];
|
||||
5994
package-lock.json
generated
5994
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
94
package.json
94
package.json
@@ -1,88 +1,26 @@
|
||||
{
|
||||
"name": "@tach/video-player",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "./dist/react/index.js",
|
||||
"types": "./dist/react/index.d.ts",
|
||||
"sideEffects": true,
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
"name": "@hublib-web/root",
|
||||
"private": true,
|
||||
"packageManager": "yarn@4.12.0",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "node ./scripts/build.mjs",
|
||||
"typecheck": "tsc -p ./tsconfig.build.json --noEmit",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build",
|
||||
"prepack": "npm run build",
|
||||
"release:patch": "npm run build && npm version patch",
|
||||
"release:minor": "npm run build && npm version minor",
|
||||
"release:major": "npm run build && npm version major"
|
||||
},
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
"./core": {
|
||||
"types": "./dist/core/index.d.ts",
|
||||
"import": "./dist/core/index.js",
|
||||
"default": "./dist/core/index.js"
|
||||
},
|
||||
"./react": {
|
||||
"types": "./dist/react/index.d.ts",
|
||||
"import": "./dist/react/index.js",
|
||||
"default": "./dist/react/index.js"
|
||||
},
|
||||
"./angular": {
|
||||
"types": "./dist/angular/index.d.ts",
|
||||
"import": "./dist/angular/index.js",
|
||||
"default": "./dist/angular/index.js"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "5.3.7",
|
||||
"antd": "5.14.2",
|
||||
"classnames": "2.5.1",
|
||||
"hls.js": "1.6.14",
|
||||
"lodash": "4.17.21",
|
||||
"qr-scanner": "1.4.2",
|
||||
"react-device-detect": "2.2.3",
|
||||
"video.js": "8.23.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0",
|
||||
"@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0",
|
||||
"@angular/forms": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@angular/common": {
|
||||
"optional": true
|
||||
},
|
||||
"@angular/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@angular/forms": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
"build": "yarn workspaces foreach -A -p --topological-dev run build",
|
||||
"typecheck": "yarn workspaces foreach -A -p --topological-dev run typecheck",
|
||||
"test": "yarn workspaces foreach -A -p --topological-dev run test",
|
||||
"clean": "yarn workspaces foreach -A -p --topological-dev run clean",
|
||||
"lint": "yarn workspaces foreach -A -p --topological-dev run lint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.37.0",
|
||||
"@storybook/addon-essentials": "8.6.14",
|
||||
"@storybook/react": "8.6.14",
|
||||
"@storybook/react-vite": "8.6.14",
|
||||
"@types/node": "22.12.0",
|
||||
"@types/react": "19.0.2",
|
||||
"@types/react-dom": "19.0.2",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"sass": "1.83.4",
|
||||
"@types/node": "^24.6.1",
|
||||
"eslint": "^9.37.0",
|
||||
"prettier": "^3.6.2",
|
||||
"storybook": "8.6.14",
|
||||
"typescript": "5.9.2",
|
||||
"vite": "5.4.14"
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^3.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
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",
|
||||
],
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user