@shival99/z-ui 2.6.10 → 2.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/shival99-z-ui-components-z-code.mjs +11 -4
- package/fesm2022/shival99-z-ui-components-z-code.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-editor.mjs +34 -20
- package/fesm2022/shival99-z-ui-components-z-editor.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-gallery.mjs +2 -2
- package/fesm2022/shival99-z-ui-components-z-gallery.mjs.map +1 -1
- package/package.json +1 -1
- package/types/shival99-z-ui-components-z-code.d.ts +2 -2
- package/types/shival99-z-ui-components-z-editor.d.ts +0 -1
- package/types/shival99-z-ui-components-z-gallery.d.ts +4 -4
|
@@ -3,6 +3,7 @@ import { inject, viewChild, input, signal, computed, effect, ChangeDetectionStra
|
|
|
3
3
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
4
4
|
import { ZButtonComponent } from '@shival99/z-ui/components/z-button';
|
|
5
5
|
import { ZIconComponent } from '@shival99/z-ui/components/z-icon';
|
|
6
|
+
import { ZSkeletonComponent } from '@shival99/z-ui/components/z-skeleton';
|
|
6
7
|
import { ZToastService } from '@shival99/z-ui/components/z-toast';
|
|
7
8
|
import { ZTranslateService } from '@shival99/z-ui/services';
|
|
8
9
|
import { zMergeClasses } from '@shival99/z-ui/utils';
|
|
@@ -112,7 +113,7 @@ class ZCodeComponent {
|
|
|
112
113
|
...(ngDevMode ? [{ debugName: "zHighlightLines" }] : /* istanbul ignore next */ []));
|
|
113
114
|
zScrollable = input(false, /* @ts-ignore */
|
|
114
115
|
...(ngDevMode ? [{ debugName: "zScrollable" }] : /* istanbul ignore next */ []));
|
|
115
|
-
/** Maximum body height in px; only applied when `zScrollable` is set. */
|
|
116
|
+
/** Maximum body height in px or a CSS length; only applied when `zScrollable` is set. */
|
|
116
117
|
zMaxHeight = input(400, /* @ts-ignore */
|
|
117
118
|
...(ngDevMode ? [{ debugName: "zMaxHeight" }] : /* istanbul ignore next */ []));
|
|
118
119
|
copied = signal(false, /* @ts-ignore */
|
|
@@ -143,7 +144,13 @@ class ZCodeComponent {
|
|
|
143
144
|
...(ngDevMode ? [{ debugName: "headerLabel" }] : /* istanbul ignore next */ []));
|
|
144
145
|
contentClasses = computed(() => zMergeClasses('overflow-x-auto p-4', this.zShowLineNumbers() ? 'z-code-numbered' : '', this.zHighlightLines().length > 0 ? 'z-code-banded' : '', this.zScrollable() ? 'overflow-y-auto' : ''), /* @ts-ignore */
|
|
145
146
|
...(ngDevMode ? [{ debugName: "contentClasses" }] : /* istanbul ignore next */ []));
|
|
146
|
-
bodyMaxHeight = computed(() =>
|
|
147
|
+
bodyMaxHeight = computed(() => {
|
|
148
|
+
if (!this.zScrollable()) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
const maxHeight = this.zMaxHeight();
|
|
152
|
+
return typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight;
|
|
153
|
+
}, /* @ts-ignore */
|
|
147
154
|
...(ngDevMode ? [{ debugName: "bodyMaxHeight" }] : /* istanbul ignore next */ []));
|
|
148
155
|
constructor() {
|
|
149
156
|
effect(() => {
|
|
@@ -207,11 +214,11 @@ class ZCodeComponent {
|
|
|
207
214
|
});
|
|
208
215
|
}
|
|
209
216
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ZCodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
210
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: ZCodeComponent, isStandalone: true, selector: "z-code", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, zTitle: { classPropertyName: "zTitle", publicName: "zTitle", isSignal: true, isRequired: false, transformFunction: null }, zLanguage: { classPropertyName: "zLanguage", publicName: "zLanguage", isSignal: true, isRequired: false, transformFunction: null }, zCode: { classPropertyName: "zCode", publicName: "zCode", isSignal: true, isRequired: false, transformFunction: null }, zTheme: { classPropertyName: "zTheme", publicName: "zTheme", isSignal: true, isRequired: false, transformFunction: null }, zShowLineNumbers: { classPropertyName: "zShowLineNumbers", publicName: "zShowLineNumbers", isSignal: true, isRequired: false, transformFunction: null }, zHighlightLines: { classPropertyName: "zHighlightLines", publicName: "zHighlightLines", isSignal: true, isRequired: false, transformFunction: null }, zScrollable: { classPropertyName: "zScrollable", publicName: "zScrollable", isSignal: true, isRequired: false, transformFunction: null }, zMaxHeight: { classPropertyName: "zMaxHeight", publicName: "zMaxHeight", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "codeEl", first: true, predicate: ["codeEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div [class]=\"wrapperClasses()\">\n <!-- Always rendered: without it a title-less block floated its copy button in a strip of empty\n padding, with nothing to anchor the button or separate it from the code. -->\n <div class=\"z-code-header border-border bg-muted/50 flex items-center justify-between gap-2 border-b px-4 py-2\">\n <div class=\"text-muted-foreground flex min-w-0 items-center gap-2\">\n <z-icon zType=\"lucideFileCode\" zSize=\"14\" class=\"shrink-0\" />\n <span class=\"truncate font-mono text-xs\">{{ headerLabel() }}</span>\n </div>\n <button\n z-button\n zType=\"ghost\"\n zSize=\"xs\"\n zIconOnly=\"true\"\n [zWave]=\"false\"\n class=\"shrink-0\"\n [attr.aria-label]=\"copied() ? 'Copied' : 'Copy code'\"\n (click)=\"copyCode()\">\n @if (copied()) {\n <z-icon zType=\"lucideCheck\" zSize=\"14\" class=\"text-green-500\" />\n } @else {\n <z-icon zType=\"lucideCopy\" zSize=\"14\" />\n }\n </button>\n </div>\n <div class=\"relative\">\n @if (isHighlighting()) {\n
|
|
217
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: ZCodeComponent, isStandalone: true, selector: "z-code", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, zTitle: { classPropertyName: "zTitle", publicName: "zTitle", isSignal: true, isRequired: false, transformFunction: null }, zLanguage: { classPropertyName: "zLanguage", publicName: "zLanguage", isSignal: true, isRequired: false, transformFunction: null }, zCode: { classPropertyName: "zCode", publicName: "zCode", isSignal: true, isRequired: false, transformFunction: null }, zTheme: { classPropertyName: "zTheme", publicName: "zTheme", isSignal: true, isRequired: false, transformFunction: null }, zShowLineNumbers: { classPropertyName: "zShowLineNumbers", publicName: "zShowLineNumbers", isSignal: true, isRequired: false, transformFunction: null }, zHighlightLines: { classPropertyName: "zHighlightLines", publicName: "zHighlightLines", isSignal: true, isRequired: false, transformFunction: null }, zScrollable: { classPropertyName: "zScrollable", publicName: "zScrollable", isSignal: true, isRequired: false, transformFunction: null }, zMaxHeight: { classPropertyName: "zMaxHeight", publicName: "zMaxHeight", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "codeEl", first: true, predicate: ["codeEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div [class]=\"wrapperClasses()\">\n <!-- Always rendered: without it a title-less block floated its copy button in a strip of empty\n padding, with nothing to anchor the button or separate it from the code. -->\n <div class=\"z-code-header border-border bg-muted/50 flex items-center justify-between gap-2 border-b px-4 py-2\">\n <div class=\"text-muted-foreground flex min-w-0 items-center gap-2\">\n <z-icon zType=\"lucideFileCode\" zSize=\"14\" class=\"shrink-0\" />\n <span class=\"truncate font-mono text-xs\">{{ headerLabel() }}</span>\n </div>\n <button\n z-button\n zType=\"ghost\"\n zSize=\"xs\"\n zIconOnly=\"true\"\n [zWave]=\"false\"\n class=\"shrink-0\"\n [attr.aria-label]=\"copied() ? 'Copied' : 'Copy code'\"\n (click)=\"copyCode()\">\n @if (copied()) {\n <z-icon zType=\"lucideCheck\" zSize=\"14\" class=\"text-green-500\" />\n } @else {\n <z-icon zType=\"lucideCopy\" zSize=\"14\" />\n }\n </button>\n </div>\n <div class=\"relative min-h-0\" [class.flex-1]=\"zScrollable()\">\n @if (isHighlighting()) {\n @if (zScrollable()) {\n <div class=\"flex h-full flex-col p-4\">\n <z-skeleton class=\"h-full\" [zRows]=\"10\" />\n </div>\n } @else {\n <div class=\"p-4\">\n <z-skeleton zType=\"text\" [zRows]=\"3\" zHeight=\"1rem\" />\n </div>\n }\n } @else {\n <div\n #codeEl\n [class]=\"contentClasses()\"\n [style.max-height]=\"bodyMaxHeight()\"\n [innerHTML]=\"highlightedCode()\"></div>\n }\n </div>\n</div>\n", styles: [":host{display:block}:host ::ng-deep .shiki{margin:0;padding:0;overflow-x:auto;background:transparent!important;background-color:transparent!important}:host ::ng-deep .shiki code{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:.875rem;line-height:1.7;display:block;counter-reset:line}:host ::ng-deep .shiki .line{display:inline-block;width:100%}:host ::ng-deep .z-code-numbered .shiki .line:before{counter-increment:line;content:counter(line);display:inline-block;width:2rem;margin-right:1rem;text-align:right;color:var(--muted-foreground);opacity:.5;-webkit-user-select:none;user-select:none}:host ::ng-deep .z-code-banded .shiki .line-highlighted{display:inline-block;width:calc(100% + 2rem);margin:0 -1rem;padding:0 1rem;background:color-mix(in oklab,var(--primary) 12%,transparent);box-shadow:inset .125rem 0 0 var(--primary)}:host-context(.dark) ::ng-deep .shiki,:host-context(.dark) ::ng-deep .shiki span{color:var(--shiki-dark, inherit)!important;font-style:var(--shiki-dark-font-style, inherit)!important;font-weight:var(--shiki-dark-font-weight, inherit)!important;text-decoration:var(--shiki-dark-text-decoration, inherit)!important}\n"], dependencies: [{ kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zAnimatedType", "zAnimate", "zAnimationTrigger", "zSize", "zStrokeWidth", "zSvg"] }, { kind: "component", type: ZButtonComponent, selector: "z-button, button[z-button], a[z-button]", inputs: ["class", "zType", "zSize", "zShape", "zLabel", "zLoading", "zDisabled", "zTypeIcon", "zAnimatedTypeIcon", "zAnimateIcon", "zAnimationTriggerIcon", "zSizeIcon", "zStrokeWidthIcon", "zWave"], exportAs: ["zButton"] }, { kind: "component", type: ZSkeletonComponent, selector: "z-skeleton", inputs: ["class", "zType", "zSize", "zWidth", "zHeight", "zRows", "zGap", "zAnimated", "zRadius"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
211
218
|
}
|
|
212
219
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ZCodeComponent, decorators: [{
|
|
213
220
|
type: Component,
|
|
214
|
-
args: [{ selector: 'z-code', imports: [ZIconComponent, ZButtonComponent], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"wrapperClasses()\">\n <!-- Always rendered: without it a title-less block floated its copy button in a strip of empty\n padding, with nothing to anchor the button or separate it from the code. -->\n <div class=\"z-code-header border-border bg-muted/50 flex items-center justify-between gap-2 border-b px-4 py-2\">\n <div class=\"text-muted-foreground flex min-w-0 items-center gap-2\">\n <z-icon zType=\"lucideFileCode\" zSize=\"14\" class=\"shrink-0\" />\n <span class=\"truncate font-mono text-xs\">{{ headerLabel() }}</span>\n </div>\n <button\n z-button\n zType=\"ghost\"\n zSize=\"xs\"\n zIconOnly=\"true\"\n [zWave]=\"false\"\n class=\"shrink-0\"\n [attr.aria-label]=\"copied() ? 'Copied' : 'Copy code'\"\n (click)=\"copyCode()\">\n @if (copied()) {\n <z-icon zType=\"lucideCheck\" zSize=\"14\" class=\"text-green-500\" />\n } @else {\n <z-icon zType=\"lucideCopy\" zSize=\"14\" />\n }\n </button>\n </div>\n <div class=\"relative\">\n @if (isHighlighting()) {\n
|
|
221
|
+
args: [{ selector: 'z-code', imports: [ZIconComponent, ZButtonComponent, ZSkeletonComponent], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"wrapperClasses()\">\n <!-- Always rendered: without it a title-less block floated its copy button in a strip of empty\n padding, with nothing to anchor the button or separate it from the code. -->\n <div class=\"z-code-header border-border bg-muted/50 flex items-center justify-between gap-2 border-b px-4 py-2\">\n <div class=\"text-muted-foreground flex min-w-0 items-center gap-2\">\n <z-icon zType=\"lucideFileCode\" zSize=\"14\" class=\"shrink-0\" />\n <span class=\"truncate font-mono text-xs\">{{ headerLabel() }}</span>\n </div>\n <button\n z-button\n zType=\"ghost\"\n zSize=\"xs\"\n zIconOnly=\"true\"\n [zWave]=\"false\"\n class=\"shrink-0\"\n [attr.aria-label]=\"copied() ? 'Copied' : 'Copy code'\"\n (click)=\"copyCode()\">\n @if (copied()) {\n <z-icon zType=\"lucideCheck\" zSize=\"14\" class=\"text-green-500\" />\n } @else {\n <z-icon zType=\"lucideCopy\" zSize=\"14\" />\n }\n </button>\n </div>\n <div class=\"relative min-h-0\" [class.flex-1]=\"zScrollable()\">\n @if (isHighlighting()) {\n @if (zScrollable()) {\n <div class=\"flex h-full flex-col p-4\">\n <z-skeleton class=\"h-full\" [zRows]=\"10\" />\n </div>\n } @else {\n <div class=\"p-4\">\n <z-skeleton zType=\"text\" [zRows]=\"3\" zHeight=\"1rem\" />\n </div>\n }\n } @else {\n <div\n #codeEl\n [class]=\"contentClasses()\"\n [style.max-height]=\"bodyMaxHeight()\"\n [innerHTML]=\"highlightedCode()\"></div>\n }\n </div>\n</div>\n", styles: [":host{display:block}:host ::ng-deep .shiki{margin:0;padding:0;overflow-x:auto;background:transparent!important;background-color:transparent!important}:host ::ng-deep .shiki code{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:.875rem;line-height:1.7;display:block;counter-reset:line}:host ::ng-deep .shiki .line{display:inline-block;width:100%}:host ::ng-deep .z-code-numbered .shiki .line:before{counter-increment:line;content:counter(line);display:inline-block;width:2rem;margin-right:1rem;text-align:right;color:var(--muted-foreground);opacity:.5;-webkit-user-select:none;user-select:none}:host ::ng-deep .z-code-banded .shiki .line-highlighted{display:inline-block;width:calc(100% + 2rem);margin:0 -1rem;padding:0 1rem;background:color-mix(in oklab,var(--primary) 12%,transparent);box-shadow:inset .125rem 0 0 var(--primary)}:host-context(.dark) ::ng-deep .shiki,:host-context(.dark) ::ng-deep .shiki span{color:var(--shiki-dark, inherit)!important;font-style:var(--shiki-dark-font-style, inherit)!important;font-weight:var(--shiki-dark-font-weight, inherit)!important;text-decoration:var(--shiki-dark-text-decoration, inherit)!important}\n"] }]
|
|
215
222
|
}], ctorParameters: () => [], propDecorators: { codeEl: [{ type: i0.ViewChild, args: ['codeEl', { isSignal: true }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], zTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "zTitle", required: false }] }], zLanguage: [{ type: i0.Input, args: [{ isSignal: true, alias: "zLanguage", required: false }] }], zCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "zCode", required: false }] }], zTheme: [{ type: i0.Input, args: [{ isSignal: true, alias: "zTheme", required: false }] }], zShowLineNumbers: [{ type: i0.Input, args: [{ isSignal: true, alias: "zShowLineNumbers", required: false }] }], zHighlightLines: [{ type: i0.Input, args: [{ isSignal: true, alias: "zHighlightLines", required: false }] }], zScrollable: [{ type: i0.Input, args: [{ isSignal: true, alias: "zScrollable", required: false }] }], zMaxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "zMaxHeight", required: false }] }] } });
|
|
216
223
|
|
|
217
224
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shival99-z-ui-components-z-code.mjs","sources":["../../../../libs/core-ui/components/z-code/z-code.utils.ts","../../../../libs/core-ui/components/z-code/z-code.component.ts","../../../../libs/core-ui/components/z-code/z-code.component.html","../../../../libs/core-ui/components/z-code/shival99-z-ui-components-z-code.ts"],"sourcesContent":["import type { BundledLanguage, BundledTheme, HighlighterGeneric, ShikiTransformer } from 'shiki';\nimport { createBundledHighlighter } from 'shiki/core';\n\nconst Z_CODE_LANGS = [\n 'typescript',\n 'javascript',\n 'html',\n 'css',\n 'scss',\n 'json',\n 'bash',\n 'shell',\n 'markdown',\n 'yaml',\n 'python',\n 'java',\n 'sql',\n 'xml',\n 'angular-ts',\n 'angular-html',\n] as const;\n\nconst Z_CODE_THEMES = ['github-dark', 'github-light', 'vitesse-dark', 'one-dark-pro'] as const;\n\nconst createZCodeHighlighter = createBundledHighlighter({\n langs: {\n typescript: () => import('shiki/dist/langs/typescript.mjs'),\n javascript: () => import('shiki/dist/langs/javascript.mjs'),\n html: () => import('shiki/dist/langs/html.mjs'),\n css: () => import('shiki/dist/langs/css.mjs'),\n scss: () => import('shiki/dist/langs/scss.mjs'),\n json: () => import('shiki/dist/langs/json.mjs'),\n bash: () => import('shiki/dist/langs/bash.mjs'),\n shell: () => import('shiki/dist/langs/shell.mjs'),\n markdown: () => import('shiki/dist/langs/markdown.mjs'),\n yaml: () => import('shiki/dist/langs/yaml.mjs'),\n python: () => import('shiki/dist/langs/python.mjs'),\n java: () => import('shiki/dist/langs/java.mjs'),\n sql: () => import('shiki/dist/langs/sql.mjs'),\n xml: () => import('shiki/dist/langs/xml.mjs'),\n 'angular-ts': () => import('shiki/dist/langs/angular-ts.mjs'),\n 'angular-html': () => import('shiki/dist/langs/angular-html.mjs'),\n },\n themes: {\n 'github-dark': () => import('shiki/dist/themes/github-dark.mjs'),\n 'github-light': () => import('shiki/dist/themes/github-light.mjs'),\n 'vitesse-dark': () => import('shiki/dist/themes/vitesse-dark.mjs'),\n 'one-dark-pro': () => import('shiki/dist/themes/one-dark-pro.mjs'),\n },\n engine: () => import('shiki/engine/javascript').then(m => m.createJavaScriptRegexEngine()),\n});\n\nexport const getHighlighter = (() => {\n let cache: Promise<HighlighterGeneric<BundledLanguage, BundledTheme>> | null = null;\n\n return async (): Promise<HighlighterGeneric<BundledLanguage, BundledTheme>> => {\n if (cache) {\n return cache;\n }\n\n cache = createZCodeHighlighter({\n themes: [...Z_CODE_THEMES],\n langs: [...Z_CODE_LANGS],\n }) as Promise<HighlighterGeneric<BundledLanguage, BundledTheme>>;\n\n return cache;\n };\n})();\n\nexport function escapeHtml(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n}\n\n/**\n * Marks the requested 1-based line numbers so the stylesheet can band them. Shiki hands each line's\n * node to `line()` along with its number, which is the only reliable way to target a line — the\n * rendered markup carries no index of its own.\n */\nexport function zCodeHighlightLines(lines: readonly number[]): ShikiTransformer {\n const wanted = new Set(lines);\n return {\n name: 'z-code-highlight-lines',\n line(node, line) {\n if (wanted.has(line)) {\n this.addClassToHast(node, 'line-highlighted');\n }\n },\n };\n}\n","import {\n AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n ElementRef,\n inject,\n input,\n signal,\n viewChild,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { ZButtonComponent } from '@shival99/z-ui/components/z-button';\nimport { ZIconComponent } from '@shival99/z-ui/components/z-icon';\nimport { ZToastService } from '@shival99/z-ui/components/z-toast';\nimport { ZTranslateService } from '@shival99/z-ui/services';\nimport { zMergeClasses } from '@shival99/z-ui/utils';\nimport type { ClassValue } from 'clsx';\nimport type { BundledLanguage, BundledTheme, CodeOptionsThemes } from 'shiki';\nimport { type ZCodeTheme } from './z-code.types';\nimport { escapeHtml, getHighlighter, zCodeHighlightLines } from './z-code.utils';\n\n@Component({\n selector: 'z-code',\n imports: [ZIconComponent, ZButtonComponent],\n standalone: true,\n templateUrl: './z-code.component.html',\n styleUrl: './z-code.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ZCodeComponent implements AfterViewInit {\n private readonly _toast = inject(ZToastService);\n private readonly _sanitizer = inject(DomSanitizer);\n private readonly _zTranslate = inject(ZTranslateService);\n\n protected readonly codeEl = viewChild<ElementRef<HTMLDivElement>>('codeEl');\n\n public readonly class = input<ClassValue>('');\n public readonly zTitle = input<string>('');\n public readonly zLanguage = input<BundledLanguage>('typescript');\n public readonly zCode = input<string>('');\n public readonly zTheme = input<ZCodeTheme | ''>('');\n public readonly zShowLineNumbers = input<boolean>(true);\n /** 1-based line numbers to band. */\n public readonly zHighlightLines = input<readonly number[]>([]);\n public readonly zScrollable = input<boolean>(false);\n /** Maximum body height in px; only applied when `zScrollable` is set. */\n public readonly zMaxHeight = input<number>(400);\n\n protected readonly copied = signal(false);\n protected readonly highlightedCode = signal<SafeHtml>('');\n protected readonly isHighlighting = signal(true);\n\n private _rawCode = '';\n\n /**\n * With no explicit `zTheme`, both palettes are rendered at once: Shiki emits the light colours\n * inline and the dark ones as `--shiki-dark` custom properties, and the stylesheet swaps between\n * them under `.dark`. Switching theme is then a repaint instead of a fresh asynchronous highlight\n * of every block on the page.\n */\n private readonly _themeOptions = computed<CodeOptionsThemes<BundledTheme>>(() => {\n const theme = this.zTheme();\n if (theme) {\n return { theme };\n }\n\n return { themes: { light: 'github-light', dark: 'github-dark' } };\n });\n\n protected readonly wrapperClasses = computed(() =>\n zMergeClasses('border-border bg-card overflow-hidden rounded-lg border', this.class())\n );\n\n /** Falls back to the language, as the header is always shown and always needs something to name. */\n protected readonly headerLabel = computed(() => this.zTitle() || this.zLanguage());\n\n protected readonly contentClasses = computed(() =>\n zMergeClasses(\n 'overflow-x-auto p-4',\n this.zShowLineNumbers() ? 'z-code-numbered' : '',\n this.zHighlightLines().length > 0 ? 'z-code-banded' : '',\n this.zScrollable() ? 'overflow-y-auto' : ''\n )\n );\n\n protected readonly bodyMaxHeight = computed(() => (this.zScrollable() ? `${this.zMaxHeight()}px` : null));\n\n constructor() {\n effect(() => {\n const code = this.zCode();\n const lang = this.zLanguage();\n const themeOptions = this._themeOptions();\n const highlightLines = this.zHighlightLines();\n\n if (!code) {\n return;\n }\n\n this._rawCode = code;\n void this._highlightCode(code, lang, themeOptions, highlightLines);\n });\n }\n\n ngAfterViewInit(): void {\n if (this.zCode()) {\n return;\n }\n\n const codeEl = this.codeEl()?.nativeElement;\n if (!codeEl) {\n return;\n }\n\n const preEl = codeEl.querySelector('pre');\n const codeContent = preEl?.querySelector('code');\n const rawText = codeContent?.textContent ?? preEl?.textContent ?? codeEl.textContent ?? '';\n\n if (!rawText.trim()) {\n return;\n }\n\n this._rawCode = rawText.trim();\n void this._highlightCode(this._rawCode, this.zLanguage(), this._themeOptions(), this.zHighlightLines());\n }\n\n private async _highlightCode(\n code: string,\n lang: BundledLanguage,\n themeOptions: CodeOptionsThemes<BundledTheme>,\n highlightLines: readonly number[]\n ): Promise<void> {\n this.isHighlighting.set(true);\n try {\n const highlighter = await getHighlighter();\n const html = highlighter.codeToHtml(code, {\n lang,\n transformers: highlightLines.length > 0 ? [zCodeHighlightLines(highlightLines)] : [],\n ...themeOptions,\n });\n const safeHtml = this._sanitizer.bypassSecurityTrustHtml(html);\n this.highlightedCode.set(safeHtml);\n } catch (error) {\n console.warn('Shiki highlighting failed, falling back to plain text:', error);\n const escapedHtml = `<pre class=\"shiki\"><code>${escapeHtml(code)}</code></pre>`;\n this.highlightedCode.set(this._sanitizer.bypassSecurityTrustHtml(escapedHtml));\n } finally {\n this.isHighlighting.set(false);\n }\n }\n\n protected copyCode(): void {\n const code = this._rawCode.trim();\n\n void navigator.clipboard.writeText(code).then(() => {\n this.copied.set(true);\n this._toast.success(this._zTranslate.instant('i18n_z_ui_code_copied'));\n setTimeout(() => {\n this.copied.set(false);\n }, 2000);\n });\n }\n}\n","<div [class]=\"wrapperClasses()\">\n <!-- Always rendered: without it a title-less block floated its copy button in a strip of empty\n padding, with nothing to anchor the button or separate it from the code. -->\n <div class=\"z-code-header border-border bg-muted/50 flex items-center justify-between gap-2 border-b px-4 py-2\">\n <div class=\"text-muted-foreground flex min-w-0 items-center gap-2\">\n <z-icon zType=\"lucideFileCode\" zSize=\"14\" class=\"shrink-0\" />\n <span class=\"truncate font-mono text-xs\">{{ headerLabel() }}</span>\n </div>\n <button\n z-button\n zType=\"ghost\"\n zSize=\"xs\"\n zIconOnly=\"true\"\n [zWave]=\"false\"\n class=\"shrink-0\"\n [attr.aria-label]=\"copied() ? 'Copied' : 'Copy code'\"\n (click)=\"copyCode()\">\n @if (copied()) {\n <z-icon zType=\"lucideCheck\" zSize=\"14\" class=\"text-green-500\" />\n } @else {\n <z-icon zType=\"lucideCopy\" zSize=\"14\" />\n }\n </button>\n </div>\n <div class=\"relative\">\n @if (isHighlighting()) {\n <div class=\"p-4\">\n <div class=\"bg-muted h-4 w-3/4 animate-pulse rounded-sm\"></div>\n <div class=\"bg-muted mt-2 h-4 w-1/2 animate-pulse rounded-sm\"></div>\n <div class=\"bg-muted mt-2 h-4 w-2/3 animate-pulse rounded-sm\"></div>\n </div>\n } @else {\n <div\n #codeEl\n [class]=\"contentClasses()\"\n [style.max-height]=\"bodyMaxHeight()\"\n [innerHTML]=\"highlightedCode()\"></div>\n }\n </div>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shival99-z-ui-components-z-code.mjs","sources":["../../../../libs/core-ui/components/z-code/z-code.utils.ts","../../../../libs/core-ui/components/z-code/z-code.component.ts","../../../../libs/core-ui/components/z-code/z-code.component.html","../../../../libs/core-ui/components/z-code/shival99-z-ui-components-z-code.ts"],"sourcesContent":["import type { BundledLanguage, BundledTheme, HighlighterGeneric, ShikiTransformer } from 'shiki';\nimport { createBundledHighlighter } from 'shiki/core';\n\nconst Z_CODE_LANGS = [\n 'typescript',\n 'javascript',\n 'html',\n 'css',\n 'scss',\n 'json',\n 'bash',\n 'shell',\n 'markdown',\n 'yaml',\n 'python',\n 'java',\n 'sql',\n 'xml',\n 'angular-ts',\n 'angular-html',\n] as const;\n\nconst Z_CODE_THEMES = ['github-dark', 'github-light', 'vitesse-dark', 'one-dark-pro'] as const;\n\nconst createZCodeHighlighter = createBundledHighlighter({\n langs: {\n typescript: () => import('shiki/dist/langs/typescript.mjs'),\n javascript: () => import('shiki/dist/langs/javascript.mjs'),\n html: () => import('shiki/dist/langs/html.mjs'),\n css: () => import('shiki/dist/langs/css.mjs'),\n scss: () => import('shiki/dist/langs/scss.mjs'),\n json: () => import('shiki/dist/langs/json.mjs'),\n bash: () => import('shiki/dist/langs/bash.mjs'),\n shell: () => import('shiki/dist/langs/shell.mjs'),\n markdown: () => import('shiki/dist/langs/markdown.mjs'),\n yaml: () => import('shiki/dist/langs/yaml.mjs'),\n python: () => import('shiki/dist/langs/python.mjs'),\n java: () => import('shiki/dist/langs/java.mjs'),\n sql: () => import('shiki/dist/langs/sql.mjs'),\n xml: () => import('shiki/dist/langs/xml.mjs'),\n 'angular-ts': () => import('shiki/dist/langs/angular-ts.mjs'),\n 'angular-html': () => import('shiki/dist/langs/angular-html.mjs'),\n },\n themes: {\n 'github-dark': () => import('shiki/dist/themes/github-dark.mjs'),\n 'github-light': () => import('shiki/dist/themes/github-light.mjs'),\n 'vitesse-dark': () => import('shiki/dist/themes/vitesse-dark.mjs'),\n 'one-dark-pro': () => import('shiki/dist/themes/one-dark-pro.mjs'),\n },\n engine: () => import('shiki/engine/javascript').then(m => m.createJavaScriptRegexEngine()),\n});\n\nexport const getHighlighter = (() => {\n let cache: Promise<HighlighterGeneric<BundledLanguage, BundledTheme>> | null = null;\n\n return async (): Promise<HighlighterGeneric<BundledLanguage, BundledTheme>> => {\n if (cache) {\n return cache;\n }\n\n cache = createZCodeHighlighter({\n themes: [...Z_CODE_THEMES],\n langs: [...Z_CODE_LANGS],\n }) as Promise<HighlighterGeneric<BundledLanguage, BundledTheme>>;\n\n return cache;\n };\n})();\n\nexport function escapeHtml(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n}\n\n/**\n * Marks the requested 1-based line numbers so the stylesheet can band them. Shiki hands each line's\n * node to `line()` along with its number, which is the only reliable way to target a line — the\n * rendered markup carries no index of its own.\n */\nexport function zCodeHighlightLines(lines: readonly number[]): ShikiTransformer {\n const wanted = new Set(lines);\n return {\n name: 'z-code-highlight-lines',\n line(node, line) {\n if (wanted.has(line)) {\n this.addClassToHast(node, 'line-highlighted');\n }\n },\n };\n}\n","import {\n AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n ElementRef,\n inject,\n input,\n signal,\n viewChild,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { ZButtonComponent } from '@shival99/z-ui/components/z-button';\nimport { ZIconComponent } from '@shival99/z-ui/components/z-icon';\nimport { ZSkeletonComponent } from '@shival99/z-ui/components/z-skeleton';\nimport { ZToastService } from '@shival99/z-ui/components/z-toast';\nimport { ZTranslateService } from '@shival99/z-ui/services';\nimport { zMergeClasses } from '@shival99/z-ui/utils';\nimport type { ClassValue } from 'clsx';\nimport type { BundledLanguage, BundledTheme, CodeOptionsThemes } from 'shiki';\nimport { type ZCodeTheme } from './z-code.types';\nimport { escapeHtml, getHighlighter, zCodeHighlightLines } from './z-code.utils';\n\n@Component({\n selector: 'z-code',\n imports: [ZIconComponent, ZButtonComponent, ZSkeletonComponent],\n standalone: true,\n templateUrl: './z-code.component.html',\n styleUrl: './z-code.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ZCodeComponent implements AfterViewInit {\n private readonly _toast = inject(ZToastService);\n private readonly _sanitizer = inject(DomSanitizer);\n private readonly _zTranslate = inject(ZTranslateService);\n\n protected readonly codeEl = viewChild<ElementRef<HTMLDivElement>>('codeEl');\n\n public readonly class = input<ClassValue>('');\n public readonly zTitle = input<string>('');\n public readonly zLanguage = input<BundledLanguage>('typescript');\n public readonly zCode = input<string>('');\n public readonly zTheme = input<ZCodeTheme | ''>('');\n public readonly zShowLineNumbers = input<boolean>(true);\n /** 1-based line numbers to band. */\n public readonly zHighlightLines = input<readonly number[]>([]);\n public readonly zScrollable = input<boolean>(false);\n /** Maximum body height in px or a CSS length; only applied when `zScrollable` is set. */\n public readonly zMaxHeight = input<number | string>(400);\n\n protected readonly copied = signal(false);\n protected readonly highlightedCode = signal<SafeHtml>('');\n protected readonly isHighlighting = signal(true);\n\n private _rawCode = '';\n\n /**\n * With no explicit `zTheme`, both palettes are rendered at once: Shiki emits the light colours\n * inline and the dark ones as `--shiki-dark` custom properties, and the stylesheet swaps between\n * them under `.dark`. Switching theme is then a repaint instead of a fresh asynchronous highlight\n * of every block on the page.\n */\n private readonly _themeOptions = computed<CodeOptionsThemes<BundledTheme>>(() => {\n const theme = this.zTheme();\n if (theme) {\n return { theme };\n }\n\n return { themes: { light: 'github-light', dark: 'github-dark' } };\n });\n\n protected readonly wrapperClasses = computed(() =>\n zMergeClasses('border-border bg-card overflow-hidden rounded-lg border', this.class())\n );\n\n /** Falls back to the language, as the header is always shown and always needs something to name. */\n protected readonly headerLabel = computed(() => this.zTitle() || this.zLanguage());\n\n protected readonly contentClasses = computed(() =>\n zMergeClasses(\n 'overflow-x-auto p-4',\n this.zShowLineNumbers() ? 'z-code-numbered' : '',\n this.zHighlightLines().length > 0 ? 'z-code-banded' : '',\n this.zScrollable() ? 'overflow-y-auto' : ''\n )\n );\n\n protected readonly bodyMaxHeight = computed(() => {\n if (!this.zScrollable()) {\n return null;\n }\n\n const maxHeight = this.zMaxHeight();\n return typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight;\n });\n\n constructor() {\n effect(() => {\n const code = this.zCode();\n const lang = this.zLanguage();\n const themeOptions = this._themeOptions();\n const highlightLines = this.zHighlightLines();\n\n if (!code) {\n return;\n }\n\n this._rawCode = code;\n void this._highlightCode(code, lang, themeOptions, highlightLines);\n });\n }\n\n ngAfterViewInit(): void {\n if (this.zCode()) {\n return;\n }\n\n const codeEl = this.codeEl()?.nativeElement;\n if (!codeEl) {\n return;\n }\n\n const preEl = codeEl.querySelector('pre');\n const codeContent = preEl?.querySelector('code');\n const rawText = codeContent?.textContent ?? preEl?.textContent ?? codeEl.textContent ?? '';\n\n if (!rawText.trim()) {\n return;\n }\n\n this._rawCode = rawText.trim();\n void this._highlightCode(this._rawCode, this.zLanguage(), this._themeOptions(), this.zHighlightLines());\n }\n\n private async _highlightCode(\n code: string,\n lang: BundledLanguage,\n themeOptions: CodeOptionsThemes<BundledTheme>,\n highlightLines: readonly number[]\n ): Promise<void> {\n this.isHighlighting.set(true);\n try {\n const highlighter = await getHighlighter();\n const html = highlighter.codeToHtml(code, {\n lang,\n transformers: highlightLines.length > 0 ? [zCodeHighlightLines(highlightLines)] : [],\n ...themeOptions,\n });\n const safeHtml = this._sanitizer.bypassSecurityTrustHtml(html);\n this.highlightedCode.set(safeHtml);\n } catch (error) {\n console.warn('Shiki highlighting failed, falling back to plain text:', error);\n const escapedHtml = `<pre class=\"shiki\"><code>${escapeHtml(code)}</code></pre>`;\n this.highlightedCode.set(this._sanitizer.bypassSecurityTrustHtml(escapedHtml));\n } finally {\n this.isHighlighting.set(false);\n }\n }\n\n protected copyCode(): void {\n const code = this._rawCode.trim();\n\n void navigator.clipboard.writeText(code).then(() => {\n this.copied.set(true);\n this._toast.success(this._zTranslate.instant('i18n_z_ui_code_copied'));\n setTimeout(() => {\n this.copied.set(false);\n }, 2000);\n });\n }\n}\n","<div [class]=\"wrapperClasses()\">\n <!-- Always rendered: without it a title-less block floated its copy button in a strip of empty\n padding, with nothing to anchor the button or separate it from the code. -->\n <div class=\"z-code-header border-border bg-muted/50 flex items-center justify-between gap-2 border-b px-4 py-2\">\n <div class=\"text-muted-foreground flex min-w-0 items-center gap-2\">\n <z-icon zType=\"lucideFileCode\" zSize=\"14\" class=\"shrink-0\" />\n <span class=\"truncate font-mono text-xs\">{{ headerLabel() }}</span>\n </div>\n <button\n z-button\n zType=\"ghost\"\n zSize=\"xs\"\n zIconOnly=\"true\"\n [zWave]=\"false\"\n class=\"shrink-0\"\n [attr.aria-label]=\"copied() ? 'Copied' : 'Copy code'\"\n (click)=\"copyCode()\">\n @if (copied()) {\n <z-icon zType=\"lucideCheck\" zSize=\"14\" class=\"text-green-500\" />\n } @else {\n <z-icon zType=\"lucideCopy\" zSize=\"14\" />\n }\n </button>\n </div>\n <div class=\"relative min-h-0\" [class.flex-1]=\"zScrollable()\">\n @if (isHighlighting()) {\n @if (zScrollable()) {\n <div class=\"flex h-full flex-col p-4\">\n <z-skeleton class=\"h-full\" [zRows]=\"10\" />\n </div>\n } @else {\n <div class=\"p-4\">\n <z-skeleton zType=\"text\" [zRows]=\"3\" zHeight=\"1rem\" />\n </div>\n }\n } @else {\n <div\n #codeEl\n [class]=\"contentClasses()\"\n [style.max-height]=\"bodyMaxHeight()\"\n [innerHTML]=\"highlightedCode()\"></div>\n }\n </div>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -359,7 +359,19 @@ class ZEditorComponent {
|
|
|
359
359
|
{
|
|
360
360
|
error: 'url',
|
|
361
361
|
message: 'i18n_z_ui_editor_invalid_url',
|
|
362
|
-
validate: value =>
|
|
362
|
+
validate: value => {
|
|
363
|
+
const trimmed = String(value ?? '').trim();
|
|
364
|
+
if (!trimmed) {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
try {
|
|
368
|
+
const url = new URL(trimmed);
|
|
369
|
+
return url.protocol === 'http:' || url.protocol === 'https:';
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
},
|
|
363
375
|
},
|
|
364
376
|
];
|
|
365
377
|
textColorSwatches = Z_EDITOR_COLOR_SWATCHES;
|
|
@@ -441,7 +453,19 @@ class ZEditorComponent {
|
|
|
441
453
|
...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
442
454
|
showToolbar = computed(() => this.zToolbar() !== false && !this.zReadonly() && !this.isDisabled(), /* @ts-ignore */
|
|
443
455
|
...(ngDevMode ? [{ debugName: "showToolbar" }] : /* istanbul ignore next */ []));
|
|
444
|
-
normalizedLinkUrl = computed(() =>
|
|
456
|
+
normalizedLinkUrl = computed(() => {
|
|
457
|
+
const trimmed = this.linkUrl().trim();
|
|
458
|
+
if (!trimmed) {
|
|
459
|
+
return '';
|
|
460
|
+
}
|
|
461
|
+
try {
|
|
462
|
+
const url = new URL(trimmed);
|
|
463
|
+
return url.protocol === 'http:' || url.protocol === 'https:' ? url.toString() : '';
|
|
464
|
+
}
|
|
465
|
+
catch {
|
|
466
|
+
return '';
|
|
467
|
+
}
|
|
468
|
+
}, /* @ts-ignore */
|
|
445
469
|
...(ngDevMode ? [{ debugName: "normalizedLinkUrl" }] : /* istanbul ignore next */ []));
|
|
446
470
|
activeTextColor = computed(() => this._getTextStyleAttribute('color') || '#111827', /* @ts-ignore */
|
|
447
471
|
...(ngDevMode ? [{ debugName: "activeTextColor" }] : /* istanbul ignore next */ []));
|
|
@@ -1118,11 +1142,14 @@ class ZEditorComponent {
|
|
|
1118
1142
|
const selectionRect = posToDOMRect(editor.view, from, to);
|
|
1119
1143
|
const menuWidth = this._floatingToolbar().nativeElement.getBoundingClientRect().width;
|
|
1120
1144
|
const activeAlign = this._getActiveAlignFromEditor(editor);
|
|
1121
|
-
const left
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1145
|
+
const { left: selectionLeft } = selectionRect;
|
|
1146
|
+
let left = selectionLeft;
|
|
1147
|
+
if (activeAlign === 'alignCenter') {
|
|
1148
|
+
left = selectionRect.left + selectionRect.width / 2 - menuWidth / 2;
|
|
1149
|
+
}
|
|
1150
|
+
else if (activeAlign === 'alignRight') {
|
|
1151
|
+
left = selectionRect.right - menuWidth;
|
|
1152
|
+
}
|
|
1126
1153
|
const rect = new DOMRect(Math.max(left, 0), selectionRect.top, menuWidth, selectionRect.height);
|
|
1127
1154
|
return this._createVirtualElement(rect);
|
|
1128
1155
|
}
|
|
@@ -1596,19 +1623,6 @@ class ZEditorComponent {
|
|
|
1596
1623
|
this._collapseSelectionToAnchor();
|
|
1597
1624
|
this._refreshEditorState();
|
|
1598
1625
|
}
|
|
1599
|
-
_normalizeUrl(value) {
|
|
1600
|
-
const trimmed = value.trim();
|
|
1601
|
-
if (!trimmed) {
|
|
1602
|
-
return '';
|
|
1603
|
-
}
|
|
1604
|
-
try {
|
|
1605
|
-
const url = new URL(trimmed);
|
|
1606
|
-
return url.protocol === 'http:' || url.protocol === 'https:' ? url.toString() : '';
|
|
1607
|
-
}
|
|
1608
|
-
catch {
|
|
1609
|
-
return '';
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
1626
|
removeLink() {
|
|
1613
1627
|
const editor = this._editor;
|
|
1614
1628
|
const selection = this._linkSelection;
|