diff --git a/apps/docs/src/app/components/component-viewer/component-viewer.component.ts b/apps/docs/src/app/components/component-viewer/component-viewer.component.ts index 511f2eeba..125b515d7 100644 --- a/apps/docs/src/app/components/component-viewer/component-viewer.component.ts +++ b/apps/docs/src/app/components/component-viewer/component-viewer.component.ts @@ -12,6 +12,7 @@ import { import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router, RouterLink, RouterLinkActive, RouterOutlet, UrlSegment } from '@angular/router'; import { KbqDividerModule } from '@koobiq/components/divider'; +import { KbqIcon } from '@koobiq/components/icon'; import { KbqLinkModule } from '@koobiq/components/link'; import { KbqModalService } from '@koobiq/components/modal'; import { KbqSidepanelService } from '@koobiq/components/sidepanel'; @@ -28,19 +29,28 @@ import { DocsStructureItemTab } from 'src/app/structure'; import { DocsDocStates } from '../../services/doc-states'; +import { docsDevVersionPlaceholder, docsKoobiqVersion } from '../../version'; import { DocsAnchorsComponent } from '../anchors/anchors.component'; import { DocsExampleViewerComponent } from '../example-viewer/example-viewer'; import { DocsLiveExampleComponent } from '../live-example/docs-live-example'; import { DocsRegisterHeaderDirective } from '../register-header/register-header.directive'; +// In local dev builds `docsKoobiqVersion` is the dev placeholder (no such git ref exists), so fall back to `main`. +const GITHUB_REPO_REF = docsKoobiqVersion === docsDevVersionPlaceholder ? 'main' : docsKoobiqVersion; + +/** Base URL of the repository ref (release tag, or `main` in dev) used to link a doc item to its source directory. */ +const GITHUB_REPO_TREE_URL = `https://github.com/koobiq/angular-components/tree/${GITHUB_REPO_REF}`; + @Component({ selector: 'docs-component-viewer', imports: [ KbqTabsModule, + KbqLinkModule, RouterOutlet, RouterLink, RouterLinkActive, - DocsRegisterHeaderDirective + DocsRegisterHeaderDirective, + KbqIcon ], templateUrl: './component-viewer.template.html', styleUrls: ['./component-viewer.scss'], @@ -93,6 +103,11 @@ export class DocsComponentViewerComponent extends DocsLocaleState { this.docStates.registerHeaderScrollContainer(this.elementRef.nativeElement); } + + /** Link to the item's source directory on GitHub, or `null` when the item has no known path. */ + protected get githubSourceUrl(): string | null { + return this.structureItem.path ? `${GITHUB_REPO_TREE_URL}/${this.structureItem.path}` : null; + } } @Directive() diff --git a/apps/docs/src/app/components/component-viewer/component-viewer.template.html b/apps/docs/src/app/components/component-viewer/component-viewer.template.html index b6807eccd..f7758de17 100644 --- a/apps/docs/src/app/components/component-viewer/component-viewer.template.html +++ b/apps/docs/src/app/components/component-viewer/component-viewer.template.html @@ -3,7 +3,7 @@ {{ structureItem.name[locale()!] }} @if (!structureItem.isGuide) { -
diff --git a/apps/docs/src/app/services/i18n.spec.ts b/apps/docs/src/app/services/i18n.spec.ts index 079e72cdd..68ae196e1 100644 --- a/apps/docs/src/app/services/i18n.spec.ts +++ b/apps/docs/src/app/services/i18n.spec.ts @@ -33,6 +33,7 @@ describe('DOCS_TRANSLATIONS', () => { improvementSuggestions: { ru: 'Предложения по улучшению', en: 'Suggestions for improvement' }, overviewTab: { ru: 'Обзор', en: 'Overview' }, examplesTab: { ru: 'Примеры', en: 'Examples' }, + viewSourceOnGitHub: { ru: 'Исходный код', en: 'Source code' }, welcomeTitle: { ru: 'Дизайн-система Koobiq', en: 'Koobiq design system' }, welcomeDescription: { diff --git a/apps/docs/src/app/services/i18n.ts b/apps/docs/src/app/services/i18n.ts index b61bd2198..9800c6624 100644 --- a/apps/docs/src/app/services/i18n.ts +++ b/apps/docs/src/app/services/i18n.ts @@ -37,6 +37,7 @@ export const DOCS_TRANSLATIONS = { improvementSuggestions: { ru: 'Предложения по улучшению', en: 'Suggestions for improvement' }, overviewTab: { ru: 'Обзор', en: 'Overview' }, examplesTab: { ru: 'Примеры', en: 'Examples' }, + viewSourceOnGitHub: { ru: 'Исходный код', en: 'Source code' }, // welcome page welcomeTitle: { ru: 'Дизайн-система Koobiq', en: 'Koobiq design system' }, diff --git a/apps/docs/src/app/structure.ts b/apps/docs/src/app/structure.ts index 371383e56..4c4e3d379 100644 --- a/apps/docs/src/app/structure.ts +++ b/apps/docs/src/app/structure.ts @@ -100,6 +100,11 @@ export type DocsStructureItem = { isGuide?: boolean; apiId?: string; svgPreview?: string; + /** + * Path to the directory containing the item's source in this repository, + * relative to the repo root (e.g. `packages/components/accordion`). + */ + path?: string; /** * NOTE! Do not edit manually, this property is autogenerated. */ @@ -276,6 +281,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Accordion', en: 'Accordion' }, + path: 'packages/components/accordion', svgPreview: 'accordion', hasApi: true, apiId: 'accordion', @@ -287,6 +293,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Actions panel', en: 'Actions panel' }, + path: 'packages/components/actions-panel', svgPreview: 'actions-panel', hasApi: true, apiId: 'actions-panel', @@ -298,6 +305,7 @@ const structure: DocsStructure = makeStructure({ ru: 'App Switcher', en: 'App Switcher' }, + path: 'packages/components/app-switcher', svgPreview: 'app-switcher', hasApi: true, apiId: 'app-switcher', @@ -321,6 +329,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Alert', en: 'Alert' }, + path: 'packages/components/alert', svgPreview: 'alerts', hasApi: true, apiId: 'alert', @@ -332,6 +341,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Autocomplete', en: 'Autocomplete' }, + path: 'packages/components/autocomplete', svgPreview: 'autocomplete', hasApi: true, apiId: 'autocomplete', @@ -343,6 +353,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Badge', en: 'Badge' }, + path: 'packages/components/badge', svgPreview: 'badges', hasApi: true, apiId: 'badge', @@ -354,6 +365,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Breadcrumbs', en: 'Breadcrumbs' }, + path: 'packages/components/breadcrumbs', svgPreview: 'breadcrumbs', hasApi: true, apiId: 'breadcrumbs', @@ -365,6 +377,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Button', en: 'Button' }, + path: 'packages/components/button', svgPreview: 'button', hasApi: true, apiId: 'button', @@ -376,6 +389,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Button toggle', en: 'Button toggle' }, + path: 'packages/components/button-toggle', svgPreview: 'button toggle', hasApi: true, apiId: 'button-toggle', @@ -387,6 +401,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Button Group', en: 'Button Group' }, + path: 'packages/components/button', hasApi: true, apiId: 'button', hasExamples: false, @@ -398,6 +413,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Checkbox', en: 'Checkbox' }, + path: 'packages/components/checkbox', svgPreview: 'checkbox', hasApi: true, apiId: 'checkbox', @@ -409,6 +425,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Clamped list', en: 'Clamped list' }, + path: 'packages/components/clamped-text', hasApi: true, apiId: 'clamped-text', hasExamples: false, @@ -420,6 +437,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Clamped text', en: 'Clamped text' }, + path: 'packages/components/clamped-text', svgPreview: 'clamped-text', hasApi: true, apiId: 'clamped-text', @@ -432,6 +450,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Code block', en: 'Code block' }, + path: 'packages/components/code-block', svgPreview: 'code-block', hasApi: true, apiId: 'code-block', @@ -443,6 +462,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Content panel', en: 'Content panel' }, + path: 'packages/components/content-panel', svgPreview: 'content-panel', hasApi: true, apiId: 'content-panel', @@ -455,6 +475,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Core', en: 'Core' }, + path: 'packages/components/core', svgPreview: 'core', hasApi: true, apiId: 'core', @@ -466,6 +487,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Datepicker', en: 'Datepicker' }, + path: 'packages/components/datepicker', svgPreview: 'datepicker', hasApi: true, apiId: 'datepicker', @@ -477,6 +499,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Divider', en: 'Divider' }, + path: 'packages/components/divider', svgPreview: 'divider', hasApi: true, apiId: 'divider', @@ -488,6 +511,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Description list', en: 'Description list' }, + path: 'packages/components/dl', svgPreview: 'description list', hasApi: true, apiId: 'dl', @@ -499,6 +523,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Dropdown', en: 'Dropdown' }, + path: 'packages/components/dropdown', svgPreview: 'dropdown', hasApi: true, apiId: 'dropdown', @@ -510,6 +535,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Dynamic translation', en: 'Dynamic translation' }, + path: 'packages/components/dynamic-translation', svgPreview: 'dynamic-translation', hasApi: true, apiId: 'dynamic-translation', @@ -522,6 +548,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Empty state', en: 'Empty state' }, + path: 'packages/components/empty-state', svgPreview: 'empty-state', hasApi: true, apiId: 'empty-state', @@ -533,6 +560,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Icon', en: 'Icon' }, + path: 'packages/components/icon', svgPreview: 'icons', hasApi: true, apiId: 'icon', @@ -544,6 +572,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Icon Button', en: 'Icon Button' }, + path: 'packages/components/icon', hasApi: true, apiId: 'icon', hasExamples: false, @@ -555,6 +584,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Icon Item', en: 'Icon Item' }, + path: 'packages/components/icon', svgPreview: 'icon-item', hasApi: true, apiId: 'icon', @@ -566,6 +596,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Inline edit', en: 'Inline edit' }, + path: 'packages/components/inline-edit', svgPreview: 'inline-edit', hasApi: true, apiId: 'inline-edit', @@ -578,6 +609,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Input', en: 'Input' }, + path: 'packages/components/input', svgPreview: 'input', hasApi: true, apiId: 'input', @@ -589,6 +621,7 @@ const structure: DocsStructure = makeStructure({ ru: 'File upload', en: 'File upload' }, + path: 'packages/components/file-upload', svgPreview: 'file upload', hasApi: true, apiId: 'file-upload', @@ -600,6 +633,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Filter Bar', en: 'Filter Bar' }, + path: 'packages/components/filter-bar', svgPreview: 'filter-bar', hasApi: true, apiId: 'filter-bar', @@ -611,6 +645,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Flag', en: 'Flag' }, + path: 'packages/components/flag', svgPreview: '', hasApi: true, apiId: 'flag', @@ -623,6 +658,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Form field', en: 'Form field' }, + path: 'packages/components/form-field', svgPreview: 'form-field', hasApi: true, apiId: 'form-field', @@ -635,6 +671,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Highlight', en: 'Highlight' }, + path: 'packages/components/core/highlight', hasApi: false, isNew: expiresAt('2026-07-15'), hasExamples: false @@ -645,6 +682,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Layout flex', en: 'Layout flex' }, + path: 'packages/components/core/styles', svgPreview: 'layout flex', hasApi: false, hasExamples: false @@ -655,6 +693,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Link', en: 'Link' }, + path: 'packages/components/link', svgPreview: 'link', hasApi: true, apiId: 'link', @@ -666,6 +705,7 @@ const structure: DocsStructure = makeStructure({ ru: 'List', en: 'List' }, + path: 'packages/components/list', svgPreview: 'list', hasApi: true, apiId: 'list', @@ -677,6 +717,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Overlay', en: 'Overlay' }, + path: 'packages/components/loader-overlay', svgPreview: 'overlay', hasApi: true, apiId: 'loader-overlay', @@ -688,6 +729,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Markdown', en: 'Markdown' }, + path: 'packages/components/markdown', svgPreview: 'markdown', hasApi: true, apiId: 'markdown', @@ -699,6 +741,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Modal', en: 'Modal' }, + path: 'packages/components/modal', svgPreview: 'modal', hasApi: true, apiId: 'modal', @@ -710,6 +753,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Navbar', en: 'Navbar' }, + path: 'packages/components/navbar', svgPreview: 'navbar', hasApi: true, apiId: 'navbar', @@ -721,6 +765,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Notification Center', en: 'Notification Center' }, + path: 'packages/components/notification-center', svgPreview: 'notification-center', hasApi: true, apiId: 'notification-center', @@ -733,6 +778,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Overflow items', en: 'Overflow items' }, + path: 'packages/components/overflow-items', hasApi: true, apiId: 'overflow-items', hasExamples: true @@ -743,6 +789,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Popover', en: 'Popover' }, + path: 'packages/components/popover', svgPreview: 'popover', hasApi: true, apiId: 'popover', @@ -754,6 +801,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Progress bar', en: 'Progress bar' }, + path: 'packages/components/progress-bar', svgPreview: 'progress-bar', hasApi: true, apiId: 'progress-bar', @@ -765,6 +813,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Progress spinner', en: 'Progress spinner' }, + path: 'packages/components/progress-spinner', svgPreview: 'progress-spinner', hasApi: true, apiId: 'progress-spinner', @@ -776,6 +825,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Radio', en: 'Radio' }, + path: 'packages/components/radio', svgPreview: 'radio', hasApi: true, apiId: 'radio', @@ -787,6 +837,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Resizer', en: 'Resizer' }, + path: 'packages/components/resizer', svgPreview: 'resizer', hasApi: true, apiId: 'resizer', @@ -798,6 +849,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Scrollbar', en: 'Scrollbar' }, + path: 'packages/components/scrollbar', svgPreview: 'scrollbar', hasApi: true, apiId: 'scrollbar', @@ -809,6 +861,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Select', en: 'Select' }, + path: 'packages/components/select', svgPreview: 'select', hasApi: true, apiId: 'select', @@ -820,6 +873,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Search expandable', en: 'Search expandable' }, + path: 'packages/components/search-expandable', svgPreview: 'search-expandable', hasApi: true, apiId: 'search-expandable', @@ -832,6 +886,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Sidebar', en: 'Sidebar' }, + path: 'packages/components/sidebar', svgPreview: 'sidebar', hasApi: true, apiId: 'sidebar', @@ -844,6 +899,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Sidepanel', en: 'Sidepanel' }, + path: 'packages/components/sidepanel', svgPreview: 'sidepanel', hasApi: true, apiId: 'sidepanel', @@ -855,6 +911,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Skeleton', en: 'Skeleton' }, + path: 'packages/components/skeleton', svgPreview: 'skeleton', hasApi: true, apiId: 'skeleton', @@ -867,6 +924,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Splitter', en: 'Splitter' }, + path: 'packages/components/splitter', svgPreview: 'splitter', hasApi: true, apiId: 'splitter', @@ -878,6 +936,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Split button', en: 'Split button' }, + path: 'packages/components/split-button', svgPreview: 'split-button', hasApi: true, apiId: 'split-button', @@ -890,6 +949,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Table', en: 'Table' }, + path: 'packages/components/table', svgPreview: 'table', hasApi: true, apiId: 'table', @@ -901,6 +961,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Tabs', en: 'Tabs' }, + path: 'packages/components/tabs', svgPreview: 'tabs', hasApi: true, apiId: 'tabs', @@ -912,6 +973,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Tag', en: 'Tag' }, + path: 'packages/components/tags', svgPreview: 'tags', hasApi: true, apiId: 'tags', @@ -923,6 +985,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Tag autocomplete', en: 'Tag autocomplete' }, + path: 'packages/components/tags', svgPreview: 'tags autocomplete', hasApi: true, apiId: 'tags', @@ -934,6 +997,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Tag input', en: 'Tag input' }, + path: 'packages/components/tags', svgPreview: 'tags input', hasApi: true, apiId: 'tags', @@ -945,6 +1009,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Tag list', en: 'Tag list' }, + path: 'packages/components/tags', svgPreview: 'tags list', hasApi: true, apiId: 'tags', @@ -956,6 +1021,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Textarea', en: 'Textarea' }, + path: 'packages/components/textarea', svgPreview: 'textarea', hasApi: true, apiId: 'textarea', @@ -967,6 +1033,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Timepicker', en: 'Timepicker' }, + path: 'packages/components/timepicker', svgPreview: 'timepicker', hasApi: true, apiId: 'timepicker', @@ -979,6 +1046,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Time range', en: 'Time range' }, + path: 'packages/components/time-range', hasApi: true, apiId: 'time-range', hasExamples: true, @@ -990,6 +1058,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Timezone', en: 'Timezone' }, + path: 'packages/components/timezone', svgPreview: 'timezone', hasApi: true, apiId: 'timezone', @@ -1001,6 +1070,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Title', en: 'Title' }, + path: 'packages/components/core/title', hasApi: true, apiId: 'title', hasExamples: false @@ -1011,6 +1081,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Toast', en: 'Toast' }, + path: 'packages/components/toast', svgPreview: 'toast', hasApi: true, apiId: 'toast', @@ -1022,6 +1093,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Toggle', en: 'Toggle' }, + path: 'packages/components/toggle', svgPreview: 'toggle', hasApi: true, apiId: 'toggle', @@ -1033,6 +1105,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Tooltip', en: 'Tooltip' }, + path: 'packages/components/tooltip', svgPreview: 'tooltip', hasApi: true, apiId: 'tooltip', @@ -1044,6 +1117,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Topbar', en: 'Topbar' }, + path: 'packages/components/top-bar', svgPreview: 'top-bar', hasApi: true, apiId: 'top-bar', @@ -1055,6 +1129,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Tree', en: 'Tree' }, + path: 'packages/components/tree', svgPreview: 'tree', hasApi: true, apiId: 'tree', @@ -1066,6 +1141,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Tree select', en: 'Tree select' }, + path: 'packages/components/tree-select', svgPreview: 'tree-select', hasApi: true, apiId: 'tree-select', @@ -1077,6 +1153,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Username', en: 'Username' }, + path: 'packages/components/username', hasApi: true, apiId: 'username', hasExamples: false, @@ -1100,6 +1177,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Date formatter', en: 'Date formatter' }, + path: 'packages/components/core/formatters/date', svgPreview: 'date', hasApi: false, apiId: 'date-formatter', @@ -1111,6 +1189,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Filesize formatter', en: 'Filesize formatter' }, + path: 'packages/components/core/formatters/filesize', svgPreview: 'formatter-filesize', hasApi: false, hasExamples: false, @@ -1133,6 +1212,7 @@ const structure: DocsStructure = makeStructure({ ru: 'Number formatter', en: 'Number formatter' }, + path: 'packages/components/core/formatters/number', svgPreview: 'number', hasApi: false, apiId: 'number-formatter', diff --git a/apps/docs/src/app/version.ts b/apps/docs/src/app/version.ts index bfc175a41..59ed5fde4 100644 --- a/apps/docs/src/app/version.ts +++ b/apps/docs/src/app/version.ts @@ -1,6 +1,9 @@ import { Version } from '@angular/core'; import { VERSION } from '@koobiq/components/core'; -const version = VERSION.full === '{{VERSION}}' ? new Version('0.0.0-dev') : VERSION; +/** Placeholder version used when the `{{VERSION}}` build-time token hasn't been replaced, i.e. in local dev builds. */ +export const docsDevVersionPlaceholder = '0.0.0-dev'; + +const version = VERSION.full === '{{VERSION}}' ? new Version(docsDevVersionPlaceholder) : VERSION; export const docsKoobiqVersion = version.full.match(/(\d+\.\d+\.\d+(?:[^\+]*))/)![1];