@atlaskit/page-layout
Version:
A collection of components which let you compose an application's page layout.
114 lines (112 loc) • 3.99 kB
JavaScript
/* eslint-disable @repo/internal/dom-events/no-unsafe-event-listeners */
/**
* @jsxRuntime classic
* @jsx jsx
*/
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled
import { css, jsx } from '@emotion/react';
import { easeOut, prefersReducedMotion } from '@atlaskit/motion';
import { DEFAULT_I18N_PROPS_SKIP_LINKS, PAGE_LAYOUT_CONTAINER_SELECTOR } from '../../common/constants';
import { useSkipLinks } from '../../controllers';
import { SkipLink } from './skip-link';
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage, @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766
const prefersReducedMotionStyles = css(prefersReducedMotion());
const skipLinkStyles = css({
margin: "var(--ds-space-250, 20px)",
padding: '0.8rem 1rem',
position: 'fixed',
zIndex: -1,
backgroundColor: "var(--ds-surface-overlay, #FFFFFF)",
border: 'none',
borderRadius: "var(--ds-radius-small, 3px)",
boxShadow: "var(--ds-shadow-overlay, 0px 8px 12px #1E1F2126, 0px 0px 1px #1E1F214f)",
insetInlineStart: -999999,
opacity: 0,
transform: 'translateY(-50%)',
transition: `transform 0.3s ${easeOut}`,
'&:focus-within': {
zIndex: 2147483640,
insetInlineStart: 0,
opacity: 1,
transform: 'translateY(0%)'
}
});
const skipLinkHeadingStyles = css({
fontWeight: "var(--ds-font-weight-semibold, 600)"
});
const skipLinkListStyles = css({
listStylePosition: 'outside',
listStyleType: 'none',
marginBlockStart: "var(--ds-space-050, 4px)",
paddingInlineStart: 0
});
const assignIndex = (num, arr) => {
if (!arr.includes(num)) {
return num;
}
return assignIndex(num + 1, arr);
};
/**
* The default label will be used when the `skipLinksLabel` attribute is not
* provided or the attribute is an empty string. If a string comprised only of
* spaces is provided, the skip link heading element will be removed, but the
* default label will still be used in `title` attribute of the skip links
* themselves.
*/
export const SkipLinkWrapper = ({
skipLinksLabel
}) => {
const {
skipLinksData
} = useSkipLinks();
if (skipLinksData.length === 0) {
return null;
}
const sortSkipLinks = arr => {
const customLinks = arr.filter(link => Number.isInteger(link.listIndex));
if (customLinks.length === 0) {
return arr;
}
const usedIndexes = customLinks.map(a => a.listIndex);
const regularLinksWithIdx = arr.filter(link => link.listIndex === undefined).map((link, idx) => {
const listIndex = assignIndex(idx, usedIndexes);
usedIndexes.push(listIndex);
return {
...link,
listIndex
};
});
return [...customLinks, ...regularLinksWithIdx].sort((a, b) => a.listIndex - b.listIndex);
};
const escapeHandler = event => {
if (event.keyCode === 27) {
const container = document.querySelector(`[${PAGE_LAYOUT_CONTAINER_SELECTOR}="true"]`
// eslint-disable-next-line @atlaskit/platform/no-direct-document-usage
);
if (container !== null) {
container.focus();
}
}
};
const attachEscHandler = () => window.addEventListener('keydown', escapeHandler, false);
const removeEscHandler = () => window.removeEventListener('keydown', escapeHandler, false);
const emptyLabelOverride = !!(skipLinksLabel !== null && skipLinksLabel !== void 0 && skipLinksLabel.match(/^\s+$/));
const label = skipLinksLabel || DEFAULT_I18N_PROPS_SKIP_LINKS;
return jsx("div", {
onFocus: attachEscHandler,
onBlur: removeEscHandler,
css: [skipLinkStyles, prefersReducedMotionStyles],
"data-skip-link-wrapper": true
}, emptyLabelOverride ? null : jsx("p", {
css: skipLinkHeadingStyles
}, label), jsx("ol", {
css: skipLinkListStyles
}, sortSkipLinks(skipLinksData).map(({
id,
skipLinkTitle
}) => jsx(SkipLink, {
key: id,
href: `#${id}`,
isFocusable: true
}, skipLinkTitle))));
};