feat(breadcrumbs): add breadcrumbs component (#22701)

resolves #22770
This commit is contained in:
Brandy Carney
2021-06-17 18:19:10 -04:00
committed by GitHub
parent faefe97da6
commit 2f6b1e4eea
39 changed files with 6798 additions and 14 deletions

View File

@ -0,0 +1,3 @@
export interface BreadcrumbCollapsedClickEventDetail {
ionShadowTarget?: HTMLElement;
}

View File

@ -0,0 +1,94 @@
@import "./breadcrumb";
@import "./breadcrumb.ios.vars";
// Breadcrumb
// --------------------------------------------------
:host {
--color: #{$breadcrumb-ios-color};
--color-active: #{$breadcrumb-ios-color-active};
--color-hover: #{$breadcrumb-ios-color-active};
--color-focused: var(--color-active);
--background-focused: #{$breadcrumb-ios-background-focused};
}
:host(.breadcrumb-active) {
font-weight: 600;
}
.breadcrumb-native {
@include border-radius(4px);
@include padding(5px, 12px, 5px, 12px);
border: 1px solid transparent;
}
// Breadcrumb: Focused
// ------------------------------------------
:host(.ion-focused) .breadcrumb-native {
@include border-radius(8px);
}
:host(.in-breadcrumbs-color.ion-focused) .breadcrumb-native,
:host(.ion-color.ion-focused) .breadcrumb-native {
background: #{current-color(base, .1)};
color: #{current-color(base)};
}
:host(.ion-focused) ::slotted(ion-icon),
:host(.in-breadcrumbs-color.ion-focused) ::slotted(ion-icon),
:host(.ion-color.ion-focused) ::slotted(ion-icon) {
color: $breadcrumb-ios-icon-color-focused;
}
// Breadcrumb Separator
// ------------------------------------------
.breadcrumb-separator {
color: $breadcrumb-ios-separator-color;
}
// Breadcrumb Slotted Icons
// ------------------------------------------
::slotted(ion-icon) {
color: $breadcrumb-ios-icon-color;
font-size: 18px;
}
::slotted(ion-icon[slot="start"]) {
@include margin(null, 8px, null, null);
}
::slotted(ion-icon[slot="end"]) {
@include margin(null, null, null, 8px);
}
:host(.breadcrumb-active) ::slotted(ion-icon) {
color: $breadcrumb-ios-icon-color-active;
}
// Breadcrumbs Collapsed Indicator
// --------------------------------------------------
.breadcrumbs-collapsed-indicator {
@include border-radius(4px);
background: $breadcrumb-ios-indicator-background;
color: $breadcrumb-ios-indicator-color;
}
.breadcrumbs-collapsed-indicator:hover {
opacity: 0.45;
}
.breadcrumbs-collapsed-indicator:focus {
background: $breadcrumb-ios-indicator-background-focused;
}

View File

@ -0,0 +1,34 @@
@import "../../themes/ionic.globals.ios";
// iOS Breadcrumb
// --------------------------------------------------
/// @prop - Color of the breadcrumb
$breadcrumb-ios-color: var(--ion-color-step-850, #2d4665) !default;
/// @prop - Color of the active breadcrumb
$breadcrumb-ios-color-active: var(--ion-text-color, #03060b) !default;
/// @prop - Background color of the focused breadcrumb
$breadcrumb-ios-background-focused: var(--ion-color-step-50, rgba(233, 237, 243, 0.7)) !default;
/// @prop - Color of the breadcrumb icon
$breadcrumb-ios-icon-color: var(--ion-color-step-400, #92a0b3) !default;
/// @prop - Color of the breadcrumb icon when active
$breadcrumb-ios-icon-color-active: var(--ion-color-step-850, #242d39) !default;
/// @prop - Color of the breadcrumb icon when focused
$breadcrumb-ios-icon-color-focused: var(--ion-color-step-750, #445b78) !default;
/// @prop - Color of the breadcrumb separator
$breadcrumb-ios-separator-color: $breadcrumb-separator-color !default;
/// @prop - Color of the breadcrumb indicator
$breadcrumb-ios-indicator-color: $breadcrumb-ios-separator-color !default;
/// @prop - Background color of the breadcrumb indicator
$breadcrumb-ios-indicator-background: var(--ion-color-step-100, #e9edf3) !default;
/// @prop - Background color of the breadcrumb indicator when focused
$breadcrumb-ios-indicator-background-focused: var(--ion-color-step-150, #d9e0ea) !default;

View File

@ -0,0 +1,89 @@
@import "./breadcrumb";
@import "./breadcrumb.md.vars";
// Breadcrumb
// --------------------------------------------------
:host {
--color: #{$breadcrumb-md-color};
--color-active: #{$breadcrumb-md-color-active};
--color-hover: #{$breadcrumb-md-color-active};
--color-focused: #{$breadcrumb-md-color-focused};
--background-focused: $breadcrumb-md-background-focused;
}
:host(.breadcrumb-active) {
font-weight: 500;
}
.breadcrumb-native {
@include padding(6px, 12px, 6px, 12px);
}
// Breadcrumb Separator
// ------------------------------------------
.breadcrumb-separator {
@include margin($breadcrumb-md-separator-margin-top, $breadcrumb-md-separator-margin-end, $breadcrumb-md-separator-margin-bottom, $breadcrumb-md-separator-margin-start);
}
// Breadcrumb: Focused
// ------------------------------------------
:host(.ion-focused) .breadcrumb-native {
@include border-radius(4px);
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2), 0px 2px 8px rgba(0, 0, 0, 0.12);
}
// Breadcrumb Separator
// ------------------------------------------
.breadcrumb-separator {
color: $breadcrumb-md-separator-color;
}
// Breadcrumb Slotted Icons
// ------------------------------------------
::slotted(ion-icon) {
color: $breadcrumb-md-icon-color;
font-size: 18px;
}
::slotted(ion-icon[slot="start"]) {
@include margin(null, 8px, null, null);
}
::slotted(ion-icon[slot="end"]) {
@include margin(null, null, null, 8px);
}
:host(.breadcrumb-active) ::slotted(ion-icon) {
color: $breadcrumb-md-icon-color-active;
}
// Breadcrumbs Collapsed Indicator
// --------------------------------------------------
.breadcrumbs-collapsed-indicator {
@include border-radius(2px);
background: $breadcrumb-md-indicator-background;
color: $breadcrumb-md-indicator-color;
}
.breadcrumbs-collapsed-indicator:hover {
opacity: 0.7;
}
.breadcrumbs-collapsed-indicator:focus {
background: $breadcrumb-md-indicator-background-focused;
}

View File

@ -0,0 +1,46 @@
@import "../../themes/ionic.globals.md";
// Material Design Breadcrumb
// --------------------------------------------------
/// @prop - Color of the breadcrumb
$breadcrumb-md-color: var(--ion-color-step-600, #677483) !default;
/// @prop - Color of the active breadcrumb
$breadcrumb-md-color-active: var(--ion-text-color, #03060b) !default;
/// @prop - Color of the focused breadcrumb
$breadcrumb-md-color-focused: var(--ion-color-step-800, #35404e) !default;
/// @prop - Background color of the focused breadcrumb
$breadcrumb-md-background-focused: var(--ion-color-step-50, #fff) !default;
/// @prop - Color of the breadcrumb icon
$breadcrumb-md-icon-color: var(--ion-color-step-550, #7d8894) !default;
/// @prop - Color of the breadcrumb icon when active
$breadcrumb-md-icon-color-active: var(--ion-color-step-850, #222d3a) !default;
/// @prop - Margin top of the breadcrumb separator
$breadcrumb-md-separator-margin-top: -1px !default;
/// @prop - Margin end of the breadcrumb separator
$breadcrumb-md-separator-margin-end: 10px !default;
/// @prop - Margin bottom of the breadcrumb separator
$breadcrumb-md-separator-margin-bottom: null !default;
/// @prop - Margin start of the breadcrumb separator
$breadcrumb-md-separator-margin-start: 10px !default;
/// @prop - Color of the breadcrumb separator
$breadcrumb-md-separator-color: $breadcrumb-separator-color !default;
/// @prop - Color of the breadcrumb indicator
$breadcrumb-md-indicator-color: $breadcrumb-md-separator-color !default;
/// @prop - Background color of the breadcrumb indicator
$breadcrumb-md-indicator-background: var(--ion-color-step-100, #eef1f3) !default;
/// @prop - Background color of the breadcrumb indicator when focused
$breadcrumb-md-indicator-background-focused: var(--ion-color-step-150, #dfe5e8) !default;

View File

@ -0,0 +1,160 @@
@import "./breadcrumb.vars";
// Breadcrumb
// --------------------------------------------------
:host {
/**
* @prop --color: Text color of the breadcrumb
* @prop --color-active: Text color of the active breadcrumb
* @prop --color-hover: Text color of the breadcrumb on hover
* @prop --color-focused: Text color of the breadcrumb when focused
* @prop --background-focused: Background color of the breadcrumb when focused
*/
display: flex;
flex: 0 0 auto;
align-items: center;
color: var(--color);
font-size: $breadcrumb-font-size;
font-weight: $breadcrumb-font-weight;
line-height: 1.5;
}
.breadcrumb-native {
@include text-inherit();
@include padding(0);
@include margin(0);
display: flex;
align-items: center;
width: 100%;
outline: none;
background: inherit;
}
:host(.breadcrumb-disabled) {
cursor: default;
opacity: .5;
pointer-events: none;
}
// Breadcrumb: Active
// ------------------------------------------
:host(.breadcrumb-active) {
color: var(--color-active);
}
// Breadcrumb: Focused
// ------------------------------------------
:host(.ion-focused) {
color: var(--color-focused);
}
:host(.ion-focused) .breadcrumb-native {
background: var(--background-focused);
}
// Breadcrumb: Hover
// ------------------------------------------
@media (any-hover: hover) {
:host(.ion-activatable:hover) {
color: var(--color-hover);
}
:host(.ion-activatable.in-breadcrumbs-color:hover),
:host(.ion-activatable.ion-color:hover) {
color: #{current-color(shade)};
}
}
// Breadcrumb Separator
// ------------------------------------------
.breadcrumb-separator {
display: inline-flex;
}
// Breadcrumb: Collapsed
// ------------------------------------------
:host(.breadcrumb-collapsed) .breadcrumb-native {
display: none;
}
// Breadcrumbs: Color
// ------------------------------------------
:host(.in-breadcrumbs-color),
:host(.in-breadcrumbs-color.breadcrumb-active) {
color: current-color(base);
}
:host(.in-breadcrumbs-color) .breadcrumb-separator {
color: current-color(base);
}
// Breadcrumb: Color
// ------------------------------------------
:host(.ion-color) {
color: current-color(base);
}
:host(.in-toolbar-color),
:host(.in-toolbar-color) .breadcrumb-separator {
color: current-color(contrast, .8);
}
:host(.in-toolbar-color.breadcrumb-active) {
color: current-color(contrast);
}
// Breadcrumbs: Collapsed Indicator
// --------------------------------------------------
.breadcrumbs-collapsed-indicator {
@include padding(0);
@include margin(0, 14px);
display: flex;
flex: 1 1 100%;
align-items: center;
justify-content: center;
width: 32px;
height: 18px;
border: 0;
outline: none;
cursor: pointer;
appearance: none;
}
.breadcrumbs-collapsed-indicator ion-icon {
margin-top: 1px;
font-size: 22px;
}

View File

@ -0,0 +1,224 @@
import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Prop, h } from '@stencil/core';
import { chevronForwardOutline, ellipsisHorizontal } from 'ionicons/icons';
import { getIonMode } from '../../global/ionic-global';
import { AnimationBuilder, BreadcrumbCollapsedClickEventDetail, Color, RouterDirection } from '../../interface';
import { inheritAttributes } from '../../utils/helpers';
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
/**
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
*
* @part native - The native HTML anchor or div element that wraps all child elements.
* @part separator - The separator element between each breadcrumb.
* @part collapsed-indicator - The indicator element that shows the breadcrumbs are collapsed.
*/
@Component({
tag: 'ion-breadcrumb',
styleUrls: {
'ios': 'breadcrumb.ios.scss',
'md': 'breadcrumb.md.scss'
},
shadow: true
})
export class Breadcrumb implements ComponentInterface {
private inheritedAttributes: { [k: string]: any } = {};
private collapsedRef?: HTMLElement;
/** @internal */
@Prop() collapsed = false;
/** @internal */
@Prop() last!: boolean;
/** @internal */
@Prop() showCollapsedIndicator!: boolean;
@Element() el!: HTMLElement;
/**
* The color to use from your application's color palette.
* Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
* For more information on colors, see [theming](/docs/theming/basics).
*/
@Prop() color?: Color;
/**
* If `true`, the breadcrumb will take on a different look to show that
* it is the currently active breadcrumb. Defaults to `true` for the
* last breadcrumb if it is not set on any.
*/
@Prop() active = false;
/**
* If `true`, the user cannot interact with the breadcrumb.
*/
@Prop() disabled = false;
/**
* This attribute instructs browsers to download a URL instead of navigating to
* it, so the user will be prompted to save it as a local file. If the attribute
* has a value, it is used as the pre-filled file name in the Save prompt
* (the user can still change the file name if they want).
*/
@Prop() download: string | undefined;
/**
* Contains a URL or a URL fragment that the hyperlink points to.
* If this property is set, an anchor tag will be rendered.
*/
@Prop() href: string | undefined;
/**
* Specifies the relationship of the target object to the link object.
* The value is a space-separated list of [link types](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types).
*/
@Prop() rel: string | undefined;
/**
* If true, show a separator between this breadcrumb and the next.
* Defaults to `true` for all breadcrumbs except the last.
*/
@Prop() separator?: boolean | undefined;
/**
* Specifies where to display the linked URL.
* Only applies when an `href` is provided.
* Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`.
*/
@Prop() target: string | undefined;
/**
* When using a router, it specifies the transition direction when navigating to
* another page using `href`.
*/
@Prop() routerDirection: RouterDirection = 'forward';
/**
* When using a router, it specifies the transition animation when navigating to
* another page using `href`.
*/
@Prop() routerAnimation: AnimationBuilder | undefined;
/**
* Emitted when the breadcrumb has focus.
*/
@Event() ionFocus!: EventEmitter<void>;
/**
* Emitted when the breadcrumb loses focus.
*/
@Event() ionBlur!: EventEmitter<void>;
/**
* Emitted when the collapsed indicator is clicked on.
* `ion-breadcrumbs` will listen for this and emit ionCollapsedClick.
* Normally we could just emit this as `ionCollapsedClick`
* and let the event bubble to `ion-breadcrumbs`,
* but if the event custom event is not set on `ion-breadcrumbs`,
* TypeScript will throw an error in user applications.
* @internal
*/
@Event() collapsedClick!: EventEmitter<BreadcrumbCollapsedClickEventDetail>;
componentWillLoad() {
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
}
private isClickable(): boolean {
return this.href !== undefined;
}
private onFocus = () => {
this.ionFocus.emit();
}
private onBlur = () => {
this.ionBlur.emit();
}
private collapsedIndicatorClick = () => {
this.collapsedClick.emit({ ionShadowTarget: this.collapsedRef });
}
render() {
const { color, active, collapsed, disabled, download, el, inheritedAttributes, last, routerAnimation, routerDirection, separator, showCollapsedIndicator, target } = this;
const clickable = this.isClickable();
const TagType = this.href === undefined ? 'span' : 'a' as any;
// Links can still be tabbed to when set to disabled if they have an href
// in order to truly disable them we can keep it as an anchor but remove the href
const href = disabled ? undefined : this.href;
const mode = getIonMode(this);
const attrs = (TagType === 'span')
? { }
: {
download,
href,
target
};
// If the breadcrumb is collapsed, check if it contains the collapsed indicator
// to show the separator as long as it isn't also the last breadcrumb
// otherwise if not collapsed use the value in separator
const showSeparator = last
? false
: collapsed
? showCollapsedIndicator && !last ? true : false
: separator;
return (
<Host
onClick={(ev: Event) => openURL(href, ev, routerDirection, routerAnimation)}
aria-disabled={disabled ? 'true' : null}
class={createColorClasses(color, {
[mode]: true,
'breadcrumb-active': active,
'breadcrumb-collapsed': collapsed,
'breadcrumb-disabled': disabled,
'in-breadcrumbs-color': hostContext('ion-breadcrumbs[color]', el),
'in-toolbar': hostContext('ion-toolbar', this.el),
'in-toolbar-color': hostContext('ion-toolbar[color]', this.el),
'ion-activatable': clickable,
'ion-focusable': clickable,
})}
>
<TagType
{...attrs}
class="breadcrumb-native"
part="native"
disabled={disabled}
onFocus={this.onFocus}
onBlur={this.onBlur}
{...inheritedAttributes}
>
<slot name="start"></slot>
<slot></slot>
<slot name="end"></slot>
</TagType>
{ showCollapsedIndicator &&
<button
part="collapsed-indicator"
onClick={() => this.collapsedIndicatorClick()}
ref={collapsedEl => this.collapsedRef = collapsedEl}
class={{
'breadcrumbs-collapsed-indicator': true,
}}
>
<ion-icon icon={ellipsisHorizontal} lazy={false}></ion-icon>
</button>
}
{ showSeparator &&
<span class="breadcrumb-separator" part="separator">
<slot name="separator">
{ mode === 'ios'
? <ion-icon icon={chevronForwardOutline} lazy={false}></ion-icon>
: <span>/</span>
}
</slot>
</span>
}
</Host>
);
}
}

View File

@ -0,0 +1,13 @@
@import "../../themes/ionic.globals";
// Breadcrumb
// --------------------------------------------------
/// @prop - Font weight of the breadcrumb
$breadcrumb-font-weight: 400 !default;
/// @prop - Font size of the breadcrumb
$breadcrumb-font-size: 16px !default;
/// @prop - Color of the breadcrumb separator
$breadcrumb-separator-color: var(--ion-color-step-550, #73849a) !default;

View File

@ -0,0 +1,69 @@
# ion-breadcrumb
A Breadcrumb is a single navigation item that is a child of the Breadcrumbs component. A breadcrumb can link elsewhere in an app or it can be plain text. Each breadcrumb has a separator between it and the next breadcrumb and can optionally contain an icon.
<!-- Auto Generated Below -->
## Properties
| Property | Attribute | Description | Type | Default |
| ----------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ----------- |
| `active` | `active` | If `true`, the breadcrumb will take on a different look to show that it is the currently active breadcrumb. Defaults to `true` for the last breadcrumb if it is not set on any. | `boolean` | `false` |
| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `undefined` |
| `disabled` | `disabled` | If `true`, the user cannot interact with the breadcrumb. | `boolean` | `false` |
| `download` | `download` | This attribute instructs browsers to download a URL instead of navigating to it, so the user will be prompted to save it as a local file. If the attribute has a value, it is used as the pre-filled file name in the Save prompt (the user can still change the file name if they want). | `string \| undefined` | `undefined` |
| `href` | `href` | Contains a URL or a URL fragment that the hyperlink points to. If this property is set, an anchor tag will be rendered. | `string \| undefined` | `undefined` |
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
| `rel` | `rel` | Specifies the relationship of the target object to the link object. The value is a space-separated list of [link types](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types). | `string \| undefined` | `undefined` |
| `routerAnimation` | -- | When using a router, it specifies the transition animation when navigating to another page using `href`. | `((baseEl: any, opts?: any) => Animation) \| undefined` | `undefined` |
| `routerDirection` | `router-direction` | When using a router, it specifies the transition direction when navigating to another page using `href`. | `"back" \| "forward" \| "root"` | `'forward'` |
| `separator` | `separator` | If true, show a separator between this breadcrumb and the next. Defaults to `true` for all breadcrumbs except the last. | `boolean \| undefined` | `undefined` |
| `target` | `target` | Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`. | `string \| undefined` | `undefined` |
## Events
| Event | Description | Type |
| ---------- | ---------------------------------------- | ------------------- |
| `ionBlur` | Emitted when the breadcrumb loses focus. | `CustomEvent<void>` |
| `ionFocus` | Emitted when the breadcrumb has focus. | `CustomEvent<void>` |
## Shadow Parts
| Part | Description |
| ----------------------- | -------------------------------------------------------------------- |
| `"collapsed-indicator"` | The indicator element that shows the breadcrumbs are collapsed. |
| `"native"` | The native HTML anchor or div element that wraps all child elements. |
| `"separator"` | The separator element between each breadcrumb. |
## CSS Custom Properties
| Name | Description |
| ---------------------- | ----------------------------------------------- |
| `--background-focused` | Background color of the breadcrumb when focused |
| `--color` | Text color of the breadcrumb |
| `--color-active` | Text color of the active breadcrumb |
| `--color-focused` | Text color of the breadcrumb when focused |
| `--color-hover` | Text color of the breadcrumb on hover |
## Dependencies
### Depends on
- ion-icon
### Graph
```mermaid
graph TD;
ion-breadcrumb --> ion-icon
style ion-breadcrumb fill:#f9f,stroke:#333,stroke-width:4px
```
----------------------------------------------
*Built with [StencilJS](https://stenciljs.com/)*