Skip to content

Commit 44f09b4

Browse files
authored
feat(docs): design-tokens page (#DS-2997) (koobiq#468)
1 parent 3605e00 commit 44f09b4

29 files changed

+1982
-54
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Clipboard } from '@angular/cdk/clipboard';
2+
import { Directive, ElementRef, inject, Input } from '@angular/core';
3+
import { KbqToastService } from '@koobiq/components/toast';
4+
5+
@Directive({
6+
standalone: true,
7+
selector: '[kbq-code-snippet]',
8+
host: {
9+
class: 'kbq-docs-code-snippet',
10+
'(click)': 'copy()'
11+
}
12+
})
13+
export class CodeSnippet {
14+
@Input() tooltip = 'Скопировать';
15+
16+
private clipboard = inject(Clipboard);
17+
private toastService = inject(KbqToastService);
18+
private element = inject(ElementRef);
19+
20+
copy() {
21+
this.clipboard.copy(this.element.nativeElement.textContent);
22+
this.showSuccessfullyCopiedToast();
23+
}
24+
25+
private showSuccessfullyCopiedToast() {
26+
this.toastService.show({ style: 'success', title: 'Скопировано' });
27+
}
28+
}

apps/docs/src/app/components/component-viewer/component-viewer.component.ts

Lines changed: 24 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
Component,
66
Directive,
77
ElementRef,
8+
inject,
89
NgZone,
910
OnDestroy,
1011
OnInit,
@@ -16,7 +17,7 @@ import { Title } from '@angular/platform-browser';
1617
import { ActivatedRoute, NavigationStart, Router, UrlSegment } from '@angular/router';
1718
import { KbqModalService } from '@koobiq/components/modal';
1819
import { KbqSidepanelService } from '@koobiq/components/sidepanel';
19-
import { Subject, filter } from 'rxjs';
20+
import { filter, Subject } from 'rxjs';
2021
import { map } from 'rxjs/operators';
2122
import { AnchorsComponent } from '../anchors/anchors.component';
2223
import { DocItem, DocumentationItems } from '../documentation-items';
@@ -83,6 +84,12 @@ export class ComponentViewerComponent extends CdkScrollable implements OnInit, O
8384

8485
@Directive()
8586
export class BaseOverviewComponent {
87+
protected routeActivated = inject(ActivatedRoute);
88+
protected docItems = inject(DocumentationItems);
89+
protected router = inject(Router);
90+
protected changeDetectorRef = inject(ChangeDetectorRef);
91+
protected titleService = inject(Title);
92+
8693
readonly animationDone = new Subject<boolean>();
8794

8895
animationState: 'fadeIn' | 'fadeOut' = 'fadeOut';
@@ -103,13 +110,7 @@ export class BaseOverviewComponent {
103110
return `docs-content/overviews/components/${this.componentDocItem.id}.html`;
104111
}
105112

106-
constructor(
107-
protected routeActivated: ActivatedRoute,
108-
protected docItems: DocumentationItems,
109-
protected router: Router,
110-
protected changeDetectorRef: ChangeDetectorRef,
111-
protected titleService: Title
112-
) {
113+
constructor() {
113114
// Listen to changes on the current route for the doc id (e.g. button/checkbox) and the
114115
// parent route for the section (koobiq/cdk).
115116
this.routeActivated
@@ -120,7 +121,7 @@ export class BaseOverviewComponent {
120121
)
121122
.subscribe((d) => (this.componentDocItem = d!));
122123

123-
this.currentUrl = this.getRoute(router.url);
124+
this.currentUrl = this.getRoute(this.router.url);
124125

125126
this.router.events
126127
.pipe(
@@ -179,7 +180,7 @@ export class BaseOverviewComponent {
179180
};
180181
}
181182

182-
const animations = [
183+
export const animations = [
183184
trigger('fadeInOut', [
184185
state('fadeIn', style({ opacity: 1 })),
185186
state('fadeOut', style({ opacity: 0 })),
@@ -208,14 +209,8 @@ export class CdkOverviewComponent extends BaseOverviewComponent {
208209
return `docs-content/cdk/${this.componentDocItem.id}.html`;
209210
}
210211

211-
constructor(
212-
routeActivated: ActivatedRoute,
213-
docItems: DocumentationItems,
214-
router: Router,
215-
ref: ChangeDetectorRef,
216-
titleService: Title
217-
) {
218-
super(routeActivated, docItems, router, ref, titleService);
212+
constructor() {
213+
super();
219214
}
220215
}
221216

@@ -236,17 +231,14 @@ export class ComponentOverviewComponent extends BaseOverviewComponent {
236231
return null;
237232
}
238233

234+
console.log(this.componentDocItem);
235+
console.log(this.currentUrl);
236+
239237
return `docs-content/overviews/${this.componentDocItem.id}.html`;
240238
}
241239

242-
constructor(
243-
routeActivated: ActivatedRoute,
244-
docItems: DocumentationItems,
245-
router: Router,
246-
ref: ChangeDetectorRef,
247-
titleService: Title
248-
) {
249-
super(routeActivated, docItems, router, ref, titleService);
240+
constructor() {
241+
super();
250242
}
251243
}
252244

@@ -270,14 +262,8 @@ export class ComponentApiComponent extends BaseOverviewComponent {
270262
return `docs-content/api-docs/components-${this.componentDocItem.apiId}.html`;
271263
}
272264

273-
constructor(
274-
routeActivated: ActivatedRoute,
275-
docItems: DocumentationItems,
276-
router: Router,
277-
ref: ChangeDetectorRef,
278-
titleService: Title
279-
) {
280-
super(routeActivated, docItems, router, ref, titleService);
265+
constructor() {
266+
super();
281267
}
282268
}
283269

@@ -301,14 +287,8 @@ export class CdkApiComponent extends BaseOverviewComponent {
301287
return `docs-content/api-docs/cdk-${this.componentDocItem.id}.html`;
302288
}
303289

304-
constructor(
305-
routeActivated: ActivatedRoute,
306-
docItems: DocumentationItems,
307-
router: Router,
308-
ref: ChangeDetectorRef,
309-
titleService: Title
310-
) {
311-
super(routeActivated, docItems, router, ref, titleService);
290+
constructor() {
291+
super();
312292
}
313293
}
314294

@@ -332,13 +312,7 @@ export class ComponentExamplesComponent extends BaseOverviewComponent {
332312
return `docs-content/examples/examples.${this.componentDocItem.id}.html`;
333313
}
334314

335-
constructor(
336-
routeActivated: ActivatedRoute,
337-
docItems: DocumentationItems,
338-
router: Router,
339-
ref: ChangeDetectorRef,
340-
titleService: Title
341-
) {
342-
super(routeActivated, docItems, router, ref, titleService);
315+
constructor() {
316+
super();
343317
}
344318
}

apps/docs/src/app/components/component-viewer/component-viewer.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { KbqSidepanelService } from '@koobiq/components/sidepanel';
99
import { KbqTabsModule } from '@koobiq/components/tabs';
1010
import { CopyButtonModule } from 'src/app/components/copy-button/copy-button';
1111
import { AnchorsModule } from '../anchors/anchors.module';
12+
import { CodeSnippet } from '../code-snippet/code-snippet';
1213
import { DocExampleViewerModule } from '../doc-example-viewer/doc-example-viewer-module';
1314
import { DocsLiveExampleModule } from '../docs-live-example/docs-live-example-module';
1415
import { DocumentationItems } from '../documentation-items';
@@ -40,7 +41,8 @@ import {
4041
SidenavModule,
4142
NavbarModule,
4243
KbqIconModule,
43-
KbqLinkModule
44+
KbqLinkModule,
45+
CodeSnippet
4446
],
4547
declarations: [
4648
ComponentViewerComponent,
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { ScrollDispatcher } from '@angular/cdk/overlay';
2+
import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
3+
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
4+
import { KbqModalService } from '@koobiq/components/modal';
5+
import { KbqSidepanelService } from '@koobiq/components/sidepanel';
6+
import { KbqTabsModule } from '@koobiq/components/tabs';
7+
import { AnchorsModule } from '../anchors/anchors.module';
8+
import { CodeSnippet } from '../code-snippet/code-snippet';
9+
import { ComponentViewerComponent } from '../component-viewer/component-viewer.component';
10+
import { DocsLiveExampleModule } from '../docs-live-example/docs-live-example-module';
11+
import { DocumentationItems } from '../documentation-items';
12+
import { DocStates } from '../doс-states';
13+
import { NavbarModule } from '../navbar';
14+
import { SidenavModule } from '../sidenav/sidenav.module';
15+
16+
@Component({
17+
standalone: true,
18+
selector: 'docs-component-viewer',
19+
template: `
20+
<div class="docs-component-header">
21+
<div class="docs-component-name" docs-header>
22+
{{ docItem.name }}
23+
</div>
24+
<div class="docs-component-navbar layout-padding-top-s">
25+
<nav [tabNavPanel]="tabNavPanel" kbqTabNavBar>
26+
@for (link of links; track link) {
27+
<a [routerLink]="link.value" kbqTabLink routerLinkActive="kbq-selected">
28+
{{ link.viewValue }}
29+
</a>
30+
}
31+
</nav>
32+
</div>
33+
</div>
34+
35+
<div #tabNavPanel="kbqTabNavPanel" kbqTabNavPanel>
36+
<router-outlet />
37+
</div>
38+
`,
39+
styleUrls: ['../component-viewer/component-viewer.scss'],
40+
host: {
41+
class: 'docs-component-viewer kbq-scrollbar',
42+
'[attr.data-docsearch-category]': 'docCategoryName'
43+
},
44+
imports: [
45+
AnchorsModule,
46+
KbqTabsModule,
47+
RouterModule,
48+
DocsLiveExampleModule,
49+
SidenavModule,
50+
NavbarModule,
51+
CodeSnippet
52+
],
53+
encapsulation: ViewEncapsulation.None
54+
})
55+
export class DesignTokensViewer extends ComponentViewerComponent implements OnInit, OnDestroy {
56+
links = [
57+
{ viewValue: 'Цвета', value: 'colors' },
58+
{ viewValue: 'Тени', value: 'shadows' },
59+
{ viewValue: 'Скругления', value: 'border-radius' },
60+
{ viewValue: 'Размеры', value: 'sizes' },
61+
{ viewValue: 'Типографика', value: 'tokens-typography' },
62+
{ viewValue: 'Инженерная палитра', value: 'palette' }
63+
];
64+
65+
constructor(
66+
routeActivated: ActivatedRoute,
67+
router: Router,
68+
docItems: DocumentationItems,
69+
sidepanelService: KbqSidepanelService,
70+
modalService: KbqModalService,
71+
docStates: DocStates,
72+
elementRef: ElementRef<HTMLElement>,
73+
scrollDispatcher: ScrollDispatcher,
74+
ngZone: NgZone
75+
) {
76+
super(
77+
routeActivated,
78+
router,
79+
docItems,
80+
sidepanelService,
81+
modalService,
82+
docStates,
83+
elementRef,
84+
scrollDispatcher,
85+
ngZone
86+
);
87+
}
88+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Component } from '@angular/core';
2+
import { AnchorsModule } from '../anchors/anchors.module';
3+
import { animations, BaseOverviewComponent } from '../component-viewer/component-viewer.component';
4+
import { DocsLiveExampleModule } from '../docs-live-example/docs-live-example-module';
5+
6+
@Component({
7+
standalone: true,
8+
selector: 'docs-design-tokens-overview',
9+
templateUrl: '../component-viewer/component-overview.template.html',
10+
host: {
11+
class: 'component-overview',
12+
'[@fadeInOut]': 'animationState',
13+
'(@fadeInOut.done)': 'animationDone.next(true)'
14+
},
15+
animations,
16+
imports: [
17+
AnchorsModule,
18+
DocsLiveExampleModule
19+
]
20+
})
21+
export class TokensOverview extends BaseOverviewComponent {
22+
get docItemUrl(): string | null {
23+
if (!this.componentDocItem) return null;
24+
25+
const currentTokensSection = this.router.parseUrl(this.router.url).root.children.primary.segments.pop()?.path;
26+
27+
return `docs-content/overviews/${currentTokensSection}.html`;
28+
}
29+
30+
constructor() {
31+
super();
32+
}
33+
}

apps/docs/src/app/components/docs-live-example/docs-live-example-module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { KbqCodeBlockModule } from '@koobiq/components/code-block';
66
import { KbqLinkModule } from '@koobiq/components/link';
77
import { KbqTabsModule } from '@koobiq/components/tabs';
88
import { KbqToolTipModule } from '@koobiq/components/tooltip';
9+
import { CodeSnippet } from '../code-snippet/code-snippet';
910
import { CopyButtonModule } from '../copy-button/copy-button';
1011
import { DocsExampleSource } from '../docs-example-source/docs-example-source';
1112
import { DocsLiveExampleViewer } from '../docs-live-example-viewer/docs-live-example-viewer';
@@ -23,7 +24,8 @@ import { DocsLiveExample } from './docs-live-example';
2324
CopyButtonModule,
2425
PortalModule,
2526
KbqCodeBlockModule,
26-
NgComponentOutlet
27+
NgComponentOutlet,
28+
CodeSnippet
2729
],
2830
declarations: [DocsLiveExample, DocsLiveExampleViewer, DocsExampleSource],
2931
exports: [DocsLiveExample, DocsLiveExampleViewer, DocsExampleSource]

apps/docs/src/app/components/docs-live-example/docs-live-example.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,17 @@ import { DocsLiveExampleViewer } from '../docs-live-example-viewer/docs-live-exa
3232
[canLoad]="false"
3333
/>
3434
</ng-template>
35+
<ng-template #codeSnippet cdkPortal let-htmlContent>
36+
<span class="kbq-mono-normal" [innerHTML]="htmlContent" kbq-code-snippet kbqTooltip="Скопировать"></span>
37+
</ng-template>
3538
`,
3639
host: {
3740
class: 'docs-live-example kbq-markdown'
3841
}
3942
})
4043
export class DocsLiveExample implements OnDestroy {
4144
@ViewChild(CdkPortal) codeTemplate: CdkPortal;
45+
@ViewChild('codeSnippet', { read: CdkPortal }) codeSnippetTemplate: CdkPortal;
4246
/** The URL of the document to display. */
4347
@Input()
4448
set documentUrl(url: string) {
@@ -119,6 +123,7 @@ export class DocsLiveExample implements OnDestroy {
119123

120124
this.loadComponents('koobiq-docs-example', DocsLiveExampleViewer);
121125
this.initCodeBlocks();
126+
this.initCodeSnippets();
122127

123128
// Resolving and creating components dynamically in Angular happens synchronously, but since
124129
// we want to emit the output if the components are actually rendered completely, we wait
@@ -164,6 +169,18 @@ export class DocsLiveExample implements OnDestroy {
164169
});
165170
}
166171

172+
private initCodeSnippets() {
173+
const selector = 'kbq-code-snippet';
174+
this.nativeElement.querySelectorAll(`[${selector}]`).forEach((element: Element) => {
175+
const { innerHTML, textContent } = element;
176+
element.innerHTML = '';
177+
178+
const portalHost = new DomPortalOutlet(element, this.componentFactoryResolver, this.appRef, this.injector);
179+
this.codeSnippetTemplate.attach(portalHost, { $implicit: innerHTML, textContent });
180+
this.portalHosts.push(portalHost);
181+
});
182+
}
183+
167184
private clearLiveExamples() {
168185
this.portalHosts.forEach((h) => h.dispose());
169186
this.portalHosts = [];

0 commit comments

Comments
 (0)