mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[HXCS-1479] Breadcrumbs as secondary entry point (#8750)
* Move breadcrumb outside of the core * Fix styling * Fix storybook
This commit is contained in:
75
lib/core/breadcrumbs/src/_stories/breadcrumb.stories.ts
Normal file
75
lib/core/breadcrumbs/src/_stories/breadcrumb.stories.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Meta, moduleMetadata, Story } from '@storybook/angular';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { BreadcrumbComponent } from '../components/breadcrumb/breadcrumb.component';
|
||||
import { BreadcrumbItemComponent } from '../components/breadcrumb-item/breadcrumb-item.component';
|
||||
import { DemoBreadcrumbComponent } from './demo-breadcrumb.component';
|
||||
import { CoreStoryModule } from '../../../src/lib/testing/core.story.module';
|
||||
|
||||
// https://stackoverflow.com/a/58210459/8820824
|
||||
type NonFunctionPropertyNames<T> = {[K in keyof T]: T[K] extends () => any ? never : K}[keyof T];
|
||||
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
|
||||
type StoryWithoutFunction<T> = NonFunctionProperties<Story<T>>;
|
||||
|
||||
function storybookCopyStory<T>( story: Story<T>, annotations?: StoryWithoutFunction<T> ): Story<T> {
|
||||
const cloned = story.bind({});
|
||||
return Object.assign(cloned, annotations);
|
||||
}
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'Core/Breadcrumb',
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [
|
||||
CoreStoryModule,
|
||||
BreadcrumbComponent,
|
||||
BreadcrumbItemComponent,
|
||||
MatButtonModule,
|
||||
MatMenuModule,
|
||||
MatIconModule
|
||||
]
|
||||
})
|
||||
],
|
||||
args: {
|
||||
compact: false,
|
||||
showBreadcrumbItemWithMenu: false
|
||||
},
|
||||
argTypes: {
|
||||
compact: {control: 'boolean'},
|
||||
showBreadcrumbItemWithMenu: {control: 'boolean'}
|
||||
}
|
||||
};
|
||||
export default meta;
|
||||
|
||||
export const breadcrumb: Story = args => ({
|
||||
component: DemoBreadcrumbComponent,
|
||||
props: args
|
||||
});
|
||||
|
||||
export const compact = storybookCopyStory(breadcrumb);
|
||||
compact.args = {
|
||||
compact: true
|
||||
};
|
||||
|
||||
export const withMenu = storybookCopyStory(breadcrumb);
|
||||
withMenu.args = {
|
||||
showBreadcrumbItemWithMenu: true
|
||||
};
|
@@ -0,0 +1,63 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-demo-breadcrumb',
|
||||
template: `
|
||||
<adf-breadcrumb [compact]="compact">
|
||||
<adf-breadcrumb-item>
|
||||
<a href="/">Home</a>
|
||||
</adf-breadcrumb-item>
|
||||
|
||||
<adf-breadcrumb-item>
|
||||
<a href="https://www.alfresco.com/">Alfresco</a>
|
||||
</adf-breadcrumb-item>
|
||||
|
||||
<adf-breadcrumb-item>
|
||||
<a href="https://www.alfresco.com">External Link 1</a>
|
||||
</adf-breadcrumb-item>
|
||||
|
||||
<adf-breadcrumb-item>
|
||||
<a href="https://www.alfresco.com/">External Link 2</a>
|
||||
</adf-breadcrumb-item>
|
||||
|
||||
<adf-breadcrumb-item>
|
||||
<a href="https://www.alfresco.com/">External Link 3</a>
|
||||
</adf-breadcrumb-item>
|
||||
|
||||
<adf-breadcrumb-item *ngIf="showBreadcrumbItemWithMenu" aria-current="location" aria-haspopup="true" >
|
||||
<div>
|
||||
Current Page
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu" aria-label="Menu">
|
||||
<mat-icon>menu_open</mat-icon>
|
||||
</button>
|
||||
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>Menu Item 1</button>
|
||||
<button mat-menu-item>Menu Item 2</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</adf-breadcrumb-item>
|
||||
</adf-breadcrumb>
|
||||
`
|
||||
})
|
||||
export class DemoBreadcrumbComponent {
|
||||
compact = false;
|
||||
showBreadcrumbItemWithMenu = false;
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, TemplateRef, ViewChild } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'adf-breadcrumb-item',
|
||||
template: `
|
||||
<ng-template #breadcrumbItemTemplate>
|
||||
<ng-content></ng-content>
|
||||
</ng-template>
|
||||
`
|
||||
})
|
||||
export class BreadcrumbItemComponent {
|
||||
@ViewChild('breadcrumbItemTemplate', { static: true })
|
||||
templateRef!: TemplateRef<any>;
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
<svg width="5" height="8" viewBox="0 0 5 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.229292 0.234315C-0.0751287 0.546734 -0.0751287 1.05327 0.229292 1.36569L2.79616 4L0.229293 6.63432C-0.0751284 6.94673 -0.0751284 7.45327 0.229293 7.76569C0.533714 8.0781 1.02728 8.0781 1.3317 7.76569L4.44943 4.56604L5.00098 4L1.3317 0.234315C1.02728 -0.0781049 0.533713 -0.0781049 0.229292 0.234315Z" fill="#6B7280"/>
|
||||
</svg>
|
After Width: | Height: | Size: 469 B |
@@ -0,0 +1,26 @@
|
||||
<ng-container>
|
||||
<nav class="adf-breadcrumb" [class.adf-breadcrumb--compact]="compact" [attr.aria-label]="'CORE.BREADCRUMBS.TITLE' | translate" >
|
||||
<ol>
|
||||
<ng-container *ngFor="let breadcrumbTemplate of selectedBreadcrumbs; last as last">
|
||||
<li adf-breadcrumb-focus class="adf-breadcrumb__item-wrapper">
|
||||
<ng-container *ngTemplateOutlet="breadcrumbTemplate"></ng-container>
|
||||
<div *ngIf="!last" class="adf-breadcrumb__chevron" [class.adf-breadcrumb__chevron-before--compact]="compact" ></div>
|
||||
</li>
|
||||
|
||||
<li *ngIf="!last && compact === true" class="adf-breadcrumb__show-all-button-wrapper">
|
||||
<button
|
||||
mat-icon-button
|
||||
(click)="toggleCompact()"
|
||||
color="primary"
|
||||
[matTooltip]="'CORE.BREADCRUMBS.SHOWALL' | translate"
|
||||
matTooltipClass="adf-tooltip"
|
||||
[attr.aria-label]="'CORE.BREADCRUMBS.SHOWALL' | translate"
|
||||
>
|
||||
<mat-icon class="adf-breadcrumb__show-all-button-icon--rotate">more_vert</mat-icon >
|
||||
</button>
|
||||
<div class="adf-breadcrumb__chevron" [class.adf-breadcrumb__chevron-after--compact]="compact" ></div>
|
||||
</li>
|
||||
</ng-container>
|
||||
</ol>
|
||||
</nav>
|
||||
</ng-container>
|
@@ -0,0 +1,50 @@
|
||||
ol {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.adf-breadcrumb__item-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.adf-breadcrumb__chevron {
|
||||
background-image: url("breadcrumb-chevron.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
width: 5px;
|
||||
height: 8px;
|
||||
margin: 0 8px;
|
||||
|
||||
:host-context([dir="rtl"]) & {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
|
||||
.adf-breadcrumb__chevron-before--compact {
|
||||
margin: 0 4px 0 8px;
|
||||
|
||||
:host-context([dir="rtl"]) & {
|
||||
margin: 0 8px 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-breadcrumb__chevron-after--compact {
|
||||
margin: 0 8px 0 4px;
|
||||
|
||||
:host-context([dir="rtl"]) & {
|
||||
margin: 0 4px 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-breadcrumb__show-all-button-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.adf-breadcrumb__show-all-button-icon--rotate {
|
||||
transform: rotate(90deg);
|
||||
}
|
@@ -0,0 +1,98 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChildren, EventEmitter, Input, OnChanges, Output, QueryList, SimpleChanges, TemplateRef, ViewChildren } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { map, startWith } from 'rxjs/operators';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { BreadcrumbFocusDirective } from '../../directives/breadcrumb-focus.directive';
|
||||
import { BreadcrumbItemComponent } from '../breadcrumb-item/breadcrumb-item.component';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'adf-breadcrumb',
|
||||
templateUrl: './breadcrumb.component.html',
|
||||
styleUrls: ['./breadcrumb.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [ CommonModule, MatIconModule, TranslateModule, MatButtonModule, MatTooltipModule ]
|
||||
})
|
||||
export class BreadcrumbComponent implements AfterContentInit, OnChanges {
|
||||
private _breadcrumbTemplateRefs: Array<TemplateRef<unknown>> = [];
|
||||
|
||||
@Input()
|
||||
compact = false;
|
||||
|
||||
@Output()
|
||||
compactChange: EventEmitter<boolean> = new EventEmitter();
|
||||
|
||||
@ViewChildren(BreadcrumbFocusDirective)
|
||||
breadcrumbFocusItems!: QueryList<BreadcrumbFocusDirective>;
|
||||
|
||||
@ContentChildren(BreadcrumbItemComponent)
|
||||
breadcrumbItems!: QueryList<BreadcrumbItemComponent>;
|
||||
|
||||
selectedBreadcrumbs: Array<TemplateRef<unknown>> = [];
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngAfterContentInit() {
|
||||
this.breadcrumbItems.changes
|
||||
.pipe(
|
||||
startWith(this.breadcrumbItems),
|
||||
map((breadcrumbItems: QueryList<BreadcrumbItemComponent>) =>
|
||||
this.mapToTemplateRefs(breadcrumbItems)
|
||||
)
|
||||
)
|
||||
.subscribe((templateRefs) => {
|
||||
this._breadcrumbTemplateRefs = templateRefs;
|
||||
this.setBreadcrumbs(templateRefs);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.compact) {
|
||||
this.setBreadcrumbs(this._breadcrumbTemplateRefs);
|
||||
}
|
||||
}
|
||||
|
||||
toggleCompact(compact = false) {
|
||||
this.compact = compact;
|
||||
this.setBreadcrumbs(this._breadcrumbTemplateRefs);
|
||||
this.compactChange.emit(this.compact);
|
||||
if (!compact) {
|
||||
this.breadcrumbFocusItems.get(1)?.focusOnFirstFocusableElement();
|
||||
}
|
||||
}
|
||||
|
||||
private setBreadcrumbs(breadcrumbs: Array<TemplateRef<unknown>>) {
|
||||
this.selectedBreadcrumbs =
|
||||
this.compact && breadcrumbs.length > 2
|
||||
? [breadcrumbs[0], breadcrumbs[breadcrumbs.length - 1]]
|
||||
: [...breadcrumbs];
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
|
||||
private mapToTemplateRefs( breadcrumbItems: QueryList<BreadcrumbItemComponent> ) {
|
||||
return breadcrumbItems
|
||||
.toArray()
|
||||
.map((breadcrumbItem) => breadcrumbItem.templateRef);
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Directive, ElementRef } from '@angular/core';
|
||||
|
||||
/** @internal */
|
||||
@Directive({
|
||||
standalone: true,
|
||||
selector: '[adf-breadcrumb-focus]',
|
||||
host: {
|
||||
class: 'adf-breadcrumb-focus'
|
||||
}
|
||||
})
|
||||
export class BreadcrumbFocusDirective {
|
||||
constructor(private elementRef: ElementRef) {}
|
||||
|
||||
focusOnFirstFocusableElement() {
|
||||
this.getFocusableElements(this.elementRef.nativeElement)[0].focus();
|
||||
}
|
||||
|
||||
private getFocusableElements(root: HTMLElement): HTMLElement[] {
|
||||
const allFocusableElements = `button, a, input, select, textarea, [tabindex]:not([tabindex="-1"])`;
|
||||
return Array.from(
|
||||
root.querySelectorAll(
|
||||
allFocusableElements
|
||||
) as NodeListOf<HTMLElement>
|
||||
).filter(
|
||||
(element) =>
|
||||
!element.hasAttribute('disabled') && element.tabIndex >= 0
|
||||
);
|
||||
}
|
||||
}
|
19
lib/core/breadcrumbs/src/index.ts
Normal file
19
lib/core/breadcrumbs/src/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { BreadcrumbComponent } from './components/breadcrumb/breadcrumb.component';
|
||||
export { BreadcrumbItemComponent } from './components/breadcrumb-item/breadcrumb-item.component';
|
33
lib/core/breadcrumbs/src/styles/_breadcrumb.theme.scss
Normal file
33
lib/core/breadcrumbs/src/styles/_breadcrumb.theme.scss
Normal file
@@ -0,0 +1,33 @@
|
||||
@use 'sass:map';
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
@mixin adf-breadcrumb-theme($theme) {
|
||||
$config: mat.get-color-config($theme);
|
||||
$foreground-palette: map.get($config, foreground);
|
||||
$primary-palette: map.get($config, primary);
|
||||
$text-color: mat.get-color-from-palette($foreground-palette, text);
|
||||
$primary: mat.get-color-from-palette($primary-palette, text);
|
||||
|
||||
adf-breadcrumb {
|
||||
.adf-breadcrumb__show-all-button-icon--rotate {
|
||||
color: mat.get-color-from-palette($primary-palette, 500);
|
||||
}
|
||||
|
||||
.adf-breadcrumb__item-wrapper {
|
||||
a,
|
||||
a:active,
|
||||
a:visited {
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
&:last-child a {
|
||||
text-decoration: none;
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
&:last-child a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user