@awsui/components-react
Version:
On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en
209 lines • 13.5 kB
JavaScript
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import clsx from 'clsx';
import { getAnalyticsMetadataAttribute } from '@awsui/component-toolkit/internal/analytics-metadata';
import InternalBox from '../box/internal';
import InternalExpandableSection from '../expandable-section/internal';
import { useInternalI18n } from '../i18n/context';
import InternalIcon from '../icon/internal';
import { isPlainLeftClick } from '../internal/events';
import { useVisualRefresh } from '../internal/hooks/use-visual-mode';
import { checkSafeUrl } from '../internal/utils/check-safe-url';
import { hasActiveLink } from './util';
import analyticsSelectors from './analytics-metadata/styles.css.js';
import styles from './styles.css.js';
import testUtilStyles from './test-classes/styles.css.js';
export function Header({ definition, activeHref, fireFollow }) {
checkSafeUrl('SideNavigation', definition.href);
const onClick = useCallback((event) => {
if (isPlainLeftClick(event)) {
fireFollow(definition, event);
}
}, [fireFollow, definition]);
const clickActionAnalyticsMetadata = {
action: 'click',
detail: {
label: `.${analyticsSelectors['header-link-text']}`,
external: 'false',
href: definition.href,
position: 'header',
},
};
return (React.createElement(React.Fragment, null,
React.createElement("h2", { className: styles.header },
React.createElement("a", { href: definition.href, className: clsx(styles['header-link'], { [styles['header-link--has-logo']]: !!definition.logo }), "aria-current": definition.href === activeHref ? 'page' : undefined, onClick: onClick, ...getAnalyticsMetadataAttribute(clickActionAnalyticsMetadata) },
definition.logo && (React.createElement("img", { className: clsx(styles['header-logo'], {
[styles['header-logo--stretched']]: !definition.text,
}), ...definition.logo })),
React.createElement("span", { className: clsx(styles['header-link-text'], analyticsSelectors['header-link-text']) }, definition.text))),
React.createElement(Divider, { isPresentational: true, variant: "header" })));
}
export function NavigationItemsList({ items, variant, activeHref, fireChange, fireFollow, position = '', }) {
const lists = [];
let currentListIndex = 0;
lists[currentListIndex] = {
listVariant: variant,
items: [],
};
items.forEach((item, index) => {
var _a, _b, _c, _d, _e;
const itemid = index + 1;
const itemPosition = `${position ? `${position},` : ''}${itemid}`;
switch (item.type) {
case 'divider': {
const dividerIndex = lists.length;
lists[dividerIndex] = {
element: (React.createElement("div", { "data-itemid": `item-${itemid}` },
React.createElement(Divider, { variant: "default" }))),
};
currentListIndex = lists.length;
lists[currentListIndex] = {
listVariant: variant,
items: [],
};
return;
}
case 'link': {
(_a = lists[currentListIndex].items) === null || _a === void 0 ? void 0 : _a.push({
element: (React.createElement("li", { key: index, "data-itemid": `item-${itemid}`, className: styles['list-item'] },
React.createElement(Link, { definition: item, activeHref: activeHref, fireChange: fireChange, fireFollow: fireFollow, position: itemPosition }))),
});
return;
}
case 'section': {
(_b = lists[currentListIndex].items) === null || _b === void 0 ? void 0 : _b.push({
element: (React.createElement("li", { key: index, "data-itemid": `item-${itemid}`, className: styles['list-item'] },
React.createElement(Section, { definition: item, activeHref: activeHref, variant: variant, fireChange: fireChange, fireFollow: fireFollow, position: itemPosition }))),
});
return;
}
case 'section-group': {
(_c = lists[currentListIndex].items) === null || _c === void 0 ? void 0 : _c.push({
element: (React.createElement("li", { key: index, "data-itemid": `item-${itemid}`, className: styles['list-item'] },
React.createElement(SectionGroup, { definition: item, activeHref: activeHref, fireChange: fireChange, fireFollow: fireFollow, position: itemPosition }))),
});
return;
}
case 'link-group': {
(_d = lists[currentListIndex].items) === null || _d === void 0 ? void 0 : _d.push({
element: (React.createElement("li", { key: index, "data-itemid": `item-${itemid}`, className: styles['list-item'] },
React.createElement(LinkGroup, { definition: item, activeHref: activeHref, fireChange: fireChange, fireFollow: fireFollow, position: itemPosition }))),
});
return;
}
case 'expandable-link-group': {
(_e = lists[currentListIndex].items) === null || _e === void 0 ? void 0 : _e.push({
element: (React.createElement("li", { key: index, "data-itemid": `item-${itemid}`, className: styles['list-item'] },
React.createElement(ExpandableLinkGroup, { definition: item, activeHref: activeHref, fireChange: fireChange, fireFollow: fireFollow, variant: variant, position: itemPosition }))),
});
return;
}
}
});
return (React.createElement(React.Fragment, null, lists.map((list, index) => {
if (!list.items || list.items.length === 0) {
return (React.createElement("div", { key: `hr-${index}`, className: clsx(styles.list, styles[`list-variant-${variant}`], {
[styles['list-variant-root--first']]: list.listVariant === 'root' && index === 0,
}) }, list.element));
}
else {
return (React.createElement("ul", { key: `list-${index}`, className: clsx(styles.list, styles[`list-variant-${list.listVariant}`], {
[styles['list-variant-root--first']]: list.listVariant === 'root' && index === 0,
}) }, list.items.map(item => item.element)));
}
})));
}
function Divider({ variant = 'default', isPresentational = false }) {
return (React.createElement("hr", { className: clsx(styles.divider, styles[`divider-${variant}`]), role: isPresentational ? 'presentation' : undefined }));
}
function Link({ definition, expanded, activeHref, fireFollow, position }) {
checkSafeUrl('SideNavigation', definition.href);
const isActive = definition.href === activeHref;
const i18n = useInternalI18n('link');
const onClick = useCallback((event) => {
if (isPlainLeftClick(event)) {
fireFollow(definition, event);
}
}, [fireFollow, definition]);
const clickActionAnalyticsMetadata = {
action: 'click',
detail: {
label: `.${analyticsSelectors['link-text']}`,
external: `${!!definition.external}`,
href: definition.href,
position,
},
};
const renderedExternalIconAriaLabel = i18n('externalIconAriaLabel', definition.externalIconAriaLabel);
return (React.createElement(React.Fragment, null,
React.createElement("a", { href: definition.href, className: clsx(styles.link, { [styles['link-active']]: isActive }), target: definition.external ? '_blank' : undefined, rel: definition.external ? 'noopener noreferrer' : undefined, "aria-expanded": expanded, "aria-current": definition.href === activeHref ? 'page' : undefined, onClick: onClick, ...getAnalyticsMetadataAttribute(clickActionAnalyticsMetadata) },
React.createElement("span", { className: analyticsSelectors['link-text'] }, definition.text),
definition.external && (React.createElement("span", { "aria-label": renderedExternalIconAriaLabel, role: renderedExternalIconAriaLabel ? 'img' : undefined },
React.createElement(InternalIcon, { name: "external", className: styles['external-icon'] })))),
definition.info && React.createElement("span", { className: clsx(styles.info, testUtilStyles.info) }, definition.info)));
}
function Section({ definition, activeHref, fireFollow, fireChange, variant, position }) {
var _a;
const [expanded, setExpanded] = useState((_a = definition.defaultExpanded) !== null && _a !== void 0 ? _a : true);
const isVisualRefresh = useVisualRefresh();
const onExpandedChange = useCallback((e) => {
fireChange(definition, e.detail.expanded);
setExpanded(e.detail.expanded);
}, [definition, fireChange]);
useEffect(() => {
var _a;
setExpanded((_a = definition.defaultExpanded) !== null && _a !== void 0 ? _a : true);
}, [definition]);
return (React.createElement(InternalExpandableSection, { variant: "footer", expanded: expanded, onChange: onExpandedChange, className: clsx(styles.section, variant === 'section-group' && styles['section--no-ident'], isVisualRefresh && styles.refresh), headerText: definition.text },
React.createElement(NavigationItemsList, { variant: "section", items: definition.items, fireFollow: fireFollow, fireChange: fireChange, activeHref: activeHref, position: position })));
}
function SectionGroup({ definition, activeHref, fireFollow, fireChange, position }) {
return (React.createElement("div", { className: styles['section-group'] },
React.createElement(InternalBox, { className: styles['section-group-title'], variant: "h3" }, definition.title),
React.createElement(NavigationItemsList, { variant: "section-group", items: definition.items, fireFollow: fireFollow, fireChange: fireChange, activeHref: activeHref, position: position })));
}
function LinkGroup({ definition, activeHref, fireFollow, fireChange, position }) {
checkSafeUrl('SideNavigation', definition.href);
return (React.createElement(React.Fragment, null,
React.createElement(Link, { definition: { type: 'link', href: definition.href, text: definition.text, info: definition.info }, fireFollow: (_, event) => fireFollow(definition, event), fireChange: fireChange, activeHref: activeHref, position: position }),
React.createElement(NavigationItemsList, { variant: "link-group", items: definition.items, fireFollow: fireFollow, fireChange: fireChange, activeHref: activeHref, position: position })));
}
function ExpandableLinkGroup({ definition, fireFollow, fireChange, activeHref, variant, position, }) {
// Check whether the definition contains an active link and memoize it to avoid
// rechecking every time.
const containsActiveLink = useMemo(() => {
return activeHref ? hasActiveLink(definition.items, activeHref) : false;
}, [activeHref, definition.items]);
const [expanded, setExpanded] = useState(() => {
var _a;
return (_a = definition.defaultExpanded) !== null && _a !== void 0 ? _a : (definition.href === activeHref || containsActiveLink);
});
const [userExpanded, setUserExpanded] = useState();
// Reset user expansion status when the items property is updated.
useEffect(() => setUserExpanded(undefined), [definition]);
// By default, the expandable section is open when there's an active link inside.
useEffect(() => {
setExpanded(definition.href === activeHref || containsActiveLink);
}, [definition.href, containsActiveLink, activeHref]);
// If the definition object itself is updated, reset the expansion state to default.
useEffect(() => {
if (definition.defaultExpanded !== undefined) {
setExpanded(definition.defaultExpanded);
}
}, [definition]);
const onExpandedChange = useCallback((e) => {
fireChange(definition, e.detail.expanded);
setUserExpanded(e.detail.expanded);
}, [definition, fireChange]);
const onHeaderFollow = (_, event) => {
fireFollow(definition, event);
setUserExpanded(true);
if (!expanded) {
fireChange(definition, true);
}
};
return (React.createElement(InternalExpandableSection, { className: clsx(styles['expandable-link-group'], variant === 'section-group' && styles['expandable-link-group--no-ident']), variant: "navigation", expanded: userExpanded !== null && userExpanded !== void 0 ? userExpanded : expanded, onChange: onExpandedChange, headerText: React.createElement(Link, { definition: { type: 'link', href: definition.href, text: definition.text }, expanded: userExpanded !== null && userExpanded !== void 0 ? userExpanded : expanded, fireFollow: onHeaderFollow, fireChange: fireChange, activeHref: activeHref, position: position }) },
React.createElement(NavigationItemsList, { variant: "expandable-link-group", items: definition.items, fireFollow: fireFollow, fireChange: fireChange, activeHref: activeHref, position: position })));
}
//# sourceMappingURL=parts.js.map