-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Expand file tree
/
Copy pathbreadcrumb.tsx
More file actions
286 lines (251 loc) · 9.33 KB
/
breadcrumb.tsx
File metadata and controls
286 lines (251 loc) · 9.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
import caretRightRegular from '@phosphor-icons/core/assets/regular/caret-right.svg';
import dotsThreeRegular from '@phosphor-icons/core/assets/regular/dots-three.svg';
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Prop, h } from '@stencil/core';
import type { Attributes } from '@utils/helpers';
import { inheritAriaAttributes, openURL } from '@utils/helpers';
import { createColorClasses, hostContext } from '@utils/theme';
import { chevronForwardOutline, ellipsisHorizontal } from 'ionicons/icons';
import { config } from '../../global/config';
import { getIonTheme } from '../../global/ionic-global';
import type { AnimationBuilder, Color } from '../../interface';
import type { RouterDirection } from '../router/utils/interface';
import type { BreadcrumbCollapsedClickEventDetail } from './breadcrumb-interface';
/**
* @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component.
* @virtualProp {"ios" | "md" | "ionic"} theme - The theme determines the visual appearance of the component.
*
* @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',
ionic: 'breadcrumb.md.scss',
},
shadow: true,
})
export class Breadcrumb implements ComponentInterface {
private inheritedAttributes: Attributes = {};
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 = inheritAriaAttributes(this.el);
}
get breadcrumbCollapsedIcon() {
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: ellipsisHorizontal,
ionic: dotsThreeRegular,
md: ellipsisHorizontal,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured breadcrumb collapsed icon or the default icon
return config.get('breadcrumbCollapsedIcon', defaultIcon);
}
get breadcrumbSeparatorIcon() {
// Determine the theme and map to default icons
const theme = getIonTheme(this);
const defaultIcons = {
ios: chevronForwardOutline,
ionic: caretRightRegular,
md: chevronForwardOutline,
};
// Get the default icon based on the theme, falling back to 'md' icon if necessary
const defaultIcon = defaultIcons[theme] || defaultIcons.md;
// Return the configured breadcrumb separator icon or the default icon
return config.get('breadcrumbSeparatorIcon', defaultIcon);
}
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,
breadcrumbCollapsedIcon,
breadcrumbSeparatorIcon,
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 theme = getIonTheme(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, {
[theme]: 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"
aria-label="Show more breadcrumbs"
onClick={() => this.collapsedIndicatorClick()}
ref={(collapsedEl) => (this.collapsedRef = collapsedEl)}
class={{
'breadcrumbs-collapsed-indicator': true,
}}
>
<ion-icon aria-hidden="true" icon={breadcrumbCollapsedIcon} lazy={false}></ion-icon>
</button>
)}
{showSeparator && (
/**
* Separators should not be announced by narrators.
* We add aria-hidden on the span so that this applies
* to any custom separators too.
*/
<span class="breadcrumb-separator" part="separator" aria-hidden="true">
<slot name="separator">
{theme === 'ios' || theme === 'ionic' ? (
<ion-icon icon={breadcrumbSeparatorIcon} lazy={false} flip-rtl></ion-icon>
) : (
<span>/</span>
)}
</slot>
</span>
)}
</Host>
);
}
}