@ilo-org/react 0.16.3 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,30 +8,182 @@ var components_ContextMenu_ContextMenu = require('../ContextMenu/ContextMenu.js'
8
8
  require('tslib');
9
9
  require('../../GlobalCtx-10114bdd.js');
10
10
 
11
- const Breadcrumb = ({ home, links }) => {
11
+ const BreadcrumbItem = ({ url, label, className, labelShown = true, }) => {
12
12
  const { prefix } = hooks_useGlobalSettings();
13
- const [showContextMenu, enableContextMenu] = React.useState(false);
14
- const [toggleMenuOpen, setContextMenuToggleOpen] = React.useState(false);
15
- const handleScreenResize = () => {
16
- enableContextMenu(window.innerWidth <= 1024); // turn on context menu when screen is resized to a smaller size
17
- };
13
+ const baseClass = `${prefix}--breadcrumb--item`;
14
+ const linkClass = `${prefix}--breadcrumb--link`;
15
+ const labelClass = `${linkClass}--label`;
16
+ const breadcrumbLinkClass = classNames(baseClass, className);
17
+ return (jsxRuntime.jsx("li", Object.assign({ className: breadcrumbLinkClass }, { children: labelShown ? (jsxRuntime.jsx("a", Object.assign({ className: linkClass, href: url }, { children: jsxRuntime.jsx("span", Object.assign({ className: labelClass }, { children: label })) }))) : (jsxRuntime.jsx("a", { "aria-label": label, className: linkClass, href: url })) })));
18
+ };
19
+ const Breadcrumb = ({ className, links, buttonLabel }) => {
20
+ const { prefix } = hooks_useGlobalSettings();
21
+ // State
22
+ const [isCtxMenuOpen, setIsCtxMenuOpen] = React.useState(false);
23
+ const [ctxMenuPosition, setCtxMenuPosition] = React.useState({
24
+ start: 0,
25
+ top: 0,
26
+ });
27
+ const [isMenuCollapsed, setIsMenuCollapsed] = React.useState(false);
28
+ // Refs
29
+ const breadcrumbsRef = React.useRef(null);
30
+ const breadcrumbsWidth = React.useRef(0);
31
+ const buttonRef = React.useRef(null);
32
+ const ctxMenuRef = React.useRef(null);
33
+ // Shortcuts to classnames
34
+ const baseClass = `${prefix}--breadcrumb`;
35
+ const innerClass = `${baseClass}--inner`;
36
+ const itemsClass = `${baseClass}--items`;
37
+ const itemClass = `${baseClass}--item`;
38
+ const itemFirstClass = `${itemClass}__first`;
39
+ const contextClass = `${baseClass}--context`;
40
+ const contextCollapsedClass = `${contextClass}__collapse`;
41
+ const contextId = `${baseClass}--menu`;
42
+ const buttonClass = `${contextClass}--button`;
43
+ // Dynamic classnames
44
+ const breadcrumbClass = classNames(baseClass, className);
45
+ const contextItemsClass = classNames(contextClass, {
46
+ [contextCollapsedClass]: isMenuCollapsed,
47
+ });
48
+ const contextMenuClass = classNames(`${contextClass}--menu`, {
49
+ [`${contextClass}--menu__visible`]: isCtxMenuOpen,
50
+ });
51
+ // Handlers
52
+ function handleButtonClick() {
53
+ setIsCtxMenuOpen(!isCtxMenuOpen);
54
+ }
55
+ const collapseMenu = React.useCallback((collapse) => {
56
+ if (collapse && !isMenuCollapsed) {
57
+ setIsMenuCollapsed(true);
58
+ }
59
+ if (!collapse && isMenuCollapsed) {
60
+ setIsMenuCollapsed(false);
61
+ }
62
+ }, [isMenuCollapsed]);
63
+ // Get the width of the breadcrumbs and store it in a ref
64
+ React.useLayoutEffect(() => {
65
+ const breadcrumbs = breadcrumbsRef === null || breadcrumbsRef === void 0 ? void 0 : breadcrumbsRef.current;
66
+ if (!breadcrumbs || !window || !document) {
67
+ return;
68
+ }
69
+ breadcrumbsWidth.current = breadcrumbs.offsetWidth;
70
+ }, []);
71
+ // Collapse or uncollapse the Context Menu depending on body width
72
+ React.useLayoutEffect(() => {
73
+ const breadcrumbs = breadcrumbsRef === null || breadcrumbsRef === void 0 ? void 0 : breadcrumbsRef.current;
74
+ if (!breadcrumbs || !window || !document) {
75
+ return;
76
+ }
77
+ const observer = new ResizeObserver((entries) => {
78
+ const bodyWidth = entries[0].contentRect.width;
79
+ if (breadcrumbsWidth.current >= bodyWidth / 1.5) {
80
+ collapseMenu(true);
81
+ }
82
+ else {
83
+ collapseMenu(false);
84
+ }
85
+ });
86
+ observer.observe(document.body);
87
+ return () => {
88
+ observer.disconnect();
89
+ };
90
+ }, [collapseMenu]);
91
+ // Position the Context Menu right below the button
92
+ React.useLayoutEffect(() => {
93
+ if (!buttonRef.current || !ctxMenuRef.current) {
94
+ return;
95
+ }
96
+ if (isMenuCollapsed && isCtxMenuOpen) {
97
+ const buttonRect = buttonRef.current.getBoundingClientRect();
98
+ const buttonCenterX = buttonRect.left + buttonRect.width / 2;
99
+ const contextMenuWidth = ctxMenuRef.current.offsetWidth;
100
+ const navStart = buttonCenterX - contextMenuWidth / 2;
101
+ const navTop = buttonRect.bottom + 16;
102
+ setCtxMenuPosition({ start: navStart, top: navTop });
103
+ }
104
+ }, [isMenuCollapsed, isCtxMenuOpen]);
105
+ // If the ContextMenu is open, close it if the user clicks outside of it
18
106
  React.useEffect(() => {
19
- window.addEventListener("resize", handleScreenResize);
20
- handleScreenResize();
107
+ function handleClickOutside(event) {
108
+ if (isCtxMenuOpen &&
109
+ ctxMenuRef.current &&
110
+ !ctxMenuRef.current.contains(event.target) &&
111
+ buttonRef.current &&
112
+ !buttonRef.current.contains(event.target)) {
113
+ setIsCtxMenuOpen(false);
114
+ }
115
+ }
116
+ document.addEventListener("mousedown", handleClickOutside);
21
117
  return () => {
22
- window.removeEventListener("resize", handleScreenResize);
118
+ document.removeEventListener("mousedown", handleClickOutside);
23
119
  };
24
- }, []);
25
- const handleContextMenuToggle = () => {
26
- setContextMenuToggleOpen(!toggleMenuOpen);
27
- };
28
- const showContext = links.length > 4 || showContextMenu ? true : false;
29
- const baseClass = `${prefix}--breadcrumb`;
30
- const BreadcrumbClasses = classNames(baseClass);
120
+ }, [isMenuCollapsed, isCtxMenuOpen]);
121
+ // Close the context menu if the breadcrumbs should un-collapse
122
+ React.useEffect(() => {
123
+ if (isCtxMenuOpen && !isMenuCollapsed) {
124
+ setIsCtxMenuOpen(false);
125
+ }
126
+ }, [isMenuCollapsed, isCtxMenuOpen]);
127
+ // handle tab navigation when the context menu is open
128
+ React.useEffect(() => {
129
+ if (!buttonRef.current || !ctxMenuRef.current) {
130
+ return;
131
+ }
132
+ function handleKeyDown(event) {
133
+ var _a, _b, _c, _d;
134
+ if (!isCtxMenuOpen || !isMenuCollapsed) {
135
+ return;
136
+ }
137
+ if (event.key === "Tab") {
138
+ const links = (_a = ctxMenuRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll("a");
139
+ const lastBreadCrumb = (_c = (_b = breadcrumbsRef.current) === null || _b === void 0 ? void 0 : _b.lastElementChild) === null || _c === void 0 ? void 0 : _c.querySelector("a");
140
+ if (!links || !lastBreadCrumb) {
141
+ return;
142
+ }
143
+ const firstLink = links[0];
144
+ const lastLink = links[links.length - 1];
145
+ // If the user is tabbing...
146
+ if (!event.shiftKey) {
147
+ // If the context button is focused then the next focusable item
148
+ // should be the first item in the context menu
149
+ if (document.activeElement === buttonRef.current) {
150
+ event.preventDefault();
151
+ firstLink.focus();
152
+ return;
153
+ }
154
+ // If the last context item is focused then the focus should go to
155
+ // the last breadcrumb item
156
+ if (document.activeElement === lastLink) {
157
+ event.preventDefault();
158
+ lastBreadCrumb.focus();
159
+ }
160
+ }
161
+ // If the user is shift-tabbing...
162
+ if (event.shiftKey) {
163
+ // If the first item is focused then the focus should go back to the context button
164
+ if (event.shiftKey && document.activeElement === firstLink) {
165
+ event.preventDefault();
166
+ (_d = buttonRef === null || buttonRef === void 0 ? void 0 : buttonRef.current) === null || _d === void 0 ? void 0 : _d.focus();
167
+ }
168
+ // If the focus is on the last breadcrumb item then the focus should go
169
+ // to the last item in the context menu
170
+ if (event.shiftKey && document.activeElement === lastBreadCrumb) {
171
+ event.preventDefault();
172
+ lastLink.focus();
173
+ }
174
+ }
175
+ }
176
+ }
177
+ document.addEventListener("keydown", handleKeyDown);
178
+ return () => {
179
+ document.removeEventListener("keydown", handleKeyDown);
180
+ };
181
+ });
182
+ // Separate out the links into first, context and last
183
+ const firstLink = links[0];
184
+ const contextLinks = links.slice(1, links.length - 1);
31
185
  const lastLink = links[links.length - 1];
32
- const menuLinks = links.slice(0, -1);
33
- return (jsxRuntime.jsx("nav", Object.assign({ className: `${BreadcrumbClasses} ${showContext ? " contextmenu" : ""}` }, { children: jsxRuntime.jsxs("ol", Object.assign({ className: `${baseClass}--items` }, { children: [jsxRuntime.jsx("li", Object.assign({ className: `${baseClass}--item home` }, { children: jsxRuntime.jsx("a", Object.assign({ className: `${baseClass}--link`, href: home.url }, { children: jsxRuntime.jsx("span", Object.assign({ className: `${baseClass}--link--label` }, { children: home.label })) })) })), jsxRuntime.jsx("li", Object.assign({ className: `${baseClass}--item--context`, onClick: handleContextMenuToggle }, { children: jsxRuntime.jsxs("ul", Object.assign({ className: `${baseClass}--items context--menu ${toggleMenuOpen ? " open" : ""}` }, { children: [showContext && jsxRuntime.jsx(components_ContextMenu_ContextMenu, { links: menuLinks }), !showContext &&
34
- menuLinks.map((link, index) => (jsxRuntime.jsx("li", Object.assign({ className: `${baseClass}--item` }, { children: jsxRuntime.jsx("a", Object.assign({ href: link.url, className: `${baseClass}--link` }, { children: jsxRuntime.jsx("span", Object.assign({ className: `${baseClass}--link--label` }, { children: link.label })) })) }), index)))] })) })), jsxRuntime.jsx("li", Object.assign({ className: `${baseClass}--item final` }, { children: jsxRuntime.jsx("a", Object.assign({ className: `${baseClass}--link`, href: lastLink.url }, { children: jsxRuntime.jsx("span", Object.assign({ className: `${baseClass}--link--label` }, { children: lastLink.label })) })) }))] })) })));
186
+ return (jsxRuntime.jsxs("nav", Object.assign({ className: breadcrumbClass }, { children: [jsxRuntime.jsx("div", Object.assign({ className: innerClass }, { children: jsxRuntime.jsxs("ol", Object.assign({ className: itemsClass, ref: breadcrumbsRef }, { children: [jsxRuntime.jsx(BreadcrumbItem, { url: firstLink.url, label: firstLink.label, labelShown: false, className: itemFirstClass }), contextLinks && contextLinks.length > 0 ? (jsxRuntime.jsxs("li", Object.assign({ className: contextItemsClass }, { children: [jsxRuntime.jsx("ol", Object.assign({ className: itemsClass }, { children: contextLinks.map((contextLink, i) => (jsxRuntime.jsx(BreadcrumbItem, { url: contextLink.url, label: contextLink.label }, i))) })), jsxRuntime.jsx("button", { "aria-label": buttonLabel, "aria-expanded": isCtxMenuOpen, "aria-controls": contextId, className: buttonClass, onClick: handleButtonClick, ref: buttonRef })] }))) : null, lastLink && (jsxRuntime.jsx(BreadcrumbItem, { url: lastLink.url, label: lastLink.label }))] })) })), jsxRuntime.jsx("div", Object.assign({ className: contextMenuClass, id: contextId, hidden: !isCtxMenuOpen, style: { left: ctxMenuPosition.start, top: ctxMenuPosition.top } }, { children: jsxRuntime.jsx(components_ContextMenu_ContextMenu, { links: contextLinks, ref: ctxMenuRef }) }))] })));
35
187
  };
36
188
 
37
189
  module.exports = Breadcrumb;
@@ -1,20 +1,20 @@
1
1
  'use strict';
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
+ var React = require('react');
4
5
  var classNames = require('classnames');
5
6
  var hooks_useGlobalSettings = require('../../hooks/useGlobalSettings.js');
6
7
  require('tslib');
7
- require('react');
8
8
  require('../../GlobalCtx-10114bdd.js');
9
9
 
10
- const ContextMenu = ({ className, links }) => {
10
+ const ContextMenu = React.forwardRef(({ className, links }, ref) => {
11
11
  const { prefix } = hooks_useGlobalSettings();
12
12
  const baseClass = `${prefix}--context-menu`;
13
13
  const contextMenuClasses = classNames(className, {
14
14
  [baseClass]: true,
15
15
  });
16
- return (jsxRuntime.jsx("ul", Object.assign({ className: contextMenuClasses }, { children: links &&
16
+ return (jsxRuntime.jsx("ol", Object.assign({ className: contextMenuClasses, ref: ref }, { children: links &&
17
17
  links.map((link, i) => (jsxRuntime.jsx("li", Object.assign({ className: `${baseClass}--item ${link.endsection ? "endsection" : ""}` }, { children: jsxRuntime.jsx("a", Object.assign({ href: link.url, className: `${baseClass}--link` }, { children: jsxRuntime.jsx("span", Object.assign({ className: `${baseClass}--label` }, { children: link.label })) })) }), `${baseClass}--item-${i}`))) })));
18
- };
18
+ });
19
19
 
20
20
  module.exports = ContextMenu;
@@ -2,10 +2,10 @@
2
2
 
3
3
  var components_ContextMenu_ContextMenu = require('./ContextMenu.js');
4
4
  require('react/jsx-runtime');
5
+ require('react');
5
6
  require('classnames');
6
7
  require('../../hooks/useGlobalSettings.js');
7
8
  require('tslib');
8
- require('react');
9
9
  require('../../GlobalCtx-10114bdd.js');
10
10
 
11
11
 
@@ -1,35 +1,187 @@
1
- import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { useState, useEffect } from 'react';
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useState, useRef, useCallback, useLayoutEffect, useEffect } from 'react';
3
3
  import useGlobalSettings from '../../hooks/useGlobalSettings.js';
4
4
  import classNames from 'classnames';
5
5
  import ContextMenu from '../ContextMenu/ContextMenu.js';
6
6
  import 'tslib';
7
7
  import '../../GlobalCtx-7fb23cfa.js';
8
8
 
9
- const Breadcrumb = ({ home, links }) => {
9
+ const BreadcrumbItem = ({ url, label, className, labelShown = true, }) => {
10
10
  const { prefix } = useGlobalSettings();
11
- const [showContextMenu, enableContextMenu] = useState(false);
12
- const [toggleMenuOpen, setContextMenuToggleOpen] = useState(false);
13
- const handleScreenResize = () => {
14
- enableContextMenu(window.innerWidth <= 1024); // turn on context menu when screen is resized to a smaller size
15
- };
11
+ const baseClass = `${prefix}--breadcrumb--item`;
12
+ const linkClass = `${prefix}--breadcrumb--link`;
13
+ const labelClass = `${linkClass}--label`;
14
+ const breadcrumbLinkClass = classNames(baseClass, className);
15
+ return (jsx("li", Object.assign({ className: breadcrumbLinkClass }, { children: labelShown ? (jsx("a", Object.assign({ className: linkClass, href: url }, { children: jsx("span", Object.assign({ className: labelClass }, { children: label })) }))) : (jsx("a", { "aria-label": label, className: linkClass, href: url })) })));
16
+ };
17
+ const Breadcrumb = ({ className, links, buttonLabel }) => {
18
+ const { prefix } = useGlobalSettings();
19
+ // State
20
+ const [isCtxMenuOpen, setIsCtxMenuOpen] = useState(false);
21
+ const [ctxMenuPosition, setCtxMenuPosition] = useState({
22
+ start: 0,
23
+ top: 0,
24
+ });
25
+ const [isMenuCollapsed, setIsMenuCollapsed] = useState(false);
26
+ // Refs
27
+ const breadcrumbsRef = useRef(null);
28
+ const breadcrumbsWidth = useRef(0);
29
+ const buttonRef = useRef(null);
30
+ const ctxMenuRef = useRef(null);
31
+ // Shortcuts to classnames
32
+ const baseClass = `${prefix}--breadcrumb`;
33
+ const innerClass = `${baseClass}--inner`;
34
+ const itemsClass = `${baseClass}--items`;
35
+ const itemClass = `${baseClass}--item`;
36
+ const itemFirstClass = `${itemClass}__first`;
37
+ const contextClass = `${baseClass}--context`;
38
+ const contextCollapsedClass = `${contextClass}__collapse`;
39
+ const contextId = `${baseClass}--menu`;
40
+ const buttonClass = `${contextClass}--button`;
41
+ // Dynamic classnames
42
+ const breadcrumbClass = classNames(baseClass, className);
43
+ const contextItemsClass = classNames(contextClass, {
44
+ [contextCollapsedClass]: isMenuCollapsed,
45
+ });
46
+ const contextMenuClass = classNames(`${contextClass}--menu`, {
47
+ [`${contextClass}--menu__visible`]: isCtxMenuOpen,
48
+ });
49
+ // Handlers
50
+ function handleButtonClick() {
51
+ setIsCtxMenuOpen(!isCtxMenuOpen);
52
+ }
53
+ const collapseMenu = useCallback((collapse) => {
54
+ if (collapse && !isMenuCollapsed) {
55
+ setIsMenuCollapsed(true);
56
+ }
57
+ if (!collapse && isMenuCollapsed) {
58
+ setIsMenuCollapsed(false);
59
+ }
60
+ }, [isMenuCollapsed]);
61
+ // Get the width of the breadcrumbs and store it in a ref
62
+ useLayoutEffect(() => {
63
+ const breadcrumbs = breadcrumbsRef === null || breadcrumbsRef === void 0 ? void 0 : breadcrumbsRef.current;
64
+ if (!breadcrumbs || !window || !document) {
65
+ return;
66
+ }
67
+ breadcrumbsWidth.current = breadcrumbs.offsetWidth;
68
+ }, []);
69
+ // Collapse or uncollapse the Context Menu depending on body width
70
+ useLayoutEffect(() => {
71
+ const breadcrumbs = breadcrumbsRef === null || breadcrumbsRef === void 0 ? void 0 : breadcrumbsRef.current;
72
+ if (!breadcrumbs || !window || !document) {
73
+ return;
74
+ }
75
+ const observer = new ResizeObserver((entries) => {
76
+ const bodyWidth = entries[0].contentRect.width;
77
+ if (breadcrumbsWidth.current >= bodyWidth / 1.5) {
78
+ collapseMenu(true);
79
+ }
80
+ else {
81
+ collapseMenu(false);
82
+ }
83
+ });
84
+ observer.observe(document.body);
85
+ return () => {
86
+ observer.disconnect();
87
+ };
88
+ }, [collapseMenu]);
89
+ // Position the Context Menu right below the button
90
+ useLayoutEffect(() => {
91
+ if (!buttonRef.current || !ctxMenuRef.current) {
92
+ return;
93
+ }
94
+ if (isMenuCollapsed && isCtxMenuOpen) {
95
+ const buttonRect = buttonRef.current.getBoundingClientRect();
96
+ const buttonCenterX = buttonRect.left + buttonRect.width / 2;
97
+ const contextMenuWidth = ctxMenuRef.current.offsetWidth;
98
+ const navStart = buttonCenterX - contextMenuWidth / 2;
99
+ const navTop = buttonRect.bottom + 16;
100
+ setCtxMenuPosition({ start: navStart, top: navTop });
101
+ }
102
+ }, [isMenuCollapsed, isCtxMenuOpen]);
103
+ // If the ContextMenu is open, close it if the user clicks outside of it
16
104
  useEffect(() => {
17
- window.addEventListener("resize", handleScreenResize);
18
- handleScreenResize();
105
+ function handleClickOutside(event) {
106
+ if (isCtxMenuOpen &&
107
+ ctxMenuRef.current &&
108
+ !ctxMenuRef.current.contains(event.target) &&
109
+ buttonRef.current &&
110
+ !buttonRef.current.contains(event.target)) {
111
+ setIsCtxMenuOpen(false);
112
+ }
113
+ }
114
+ document.addEventListener("mousedown", handleClickOutside);
19
115
  return () => {
20
- window.removeEventListener("resize", handleScreenResize);
116
+ document.removeEventListener("mousedown", handleClickOutside);
21
117
  };
22
- }, []);
23
- const handleContextMenuToggle = () => {
24
- setContextMenuToggleOpen(!toggleMenuOpen);
25
- };
26
- const showContext = links.length > 4 || showContextMenu ? true : false;
27
- const baseClass = `${prefix}--breadcrumb`;
28
- const BreadcrumbClasses = classNames(baseClass);
118
+ }, [isMenuCollapsed, isCtxMenuOpen]);
119
+ // Close the context menu if the breadcrumbs should un-collapse
120
+ useEffect(() => {
121
+ if (isCtxMenuOpen && !isMenuCollapsed) {
122
+ setIsCtxMenuOpen(false);
123
+ }
124
+ }, [isMenuCollapsed, isCtxMenuOpen]);
125
+ // handle tab navigation when the context menu is open
126
+ useEffect(() => {
127
+ if (!buttonRef.current || !ctxMenuRef.current) {
128
+ return;
129
+ }
130
+ function handleKeyDown(event) {
131
+ var _a, _b, _c, _d;
132
+ if (!isCtxMenuOpen || !isMenuCollapsed) {
133
+ return;
134
+ }
135
+ if (event.key === "Tab") {
136
+ const links = (_a = ctxMenuRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll("a");
137
+ const lastBreadCrumb = (_c = (_b = breadcrumbsRef.current) === null || _b === void 0 ? void 0 : _b.lastElementChild) === null || _c === void 0 ? void 0 : _c.querySelector("a");
138
+ if (!links || !lastBreadCrumb) {
139
+ return;
140
+ }
141
+ const firstLink = links[0];
142
+ const lastLink = links[links.length - 1];
143
+ // If the user is tabbing...
144
+ if (!event.shiftKey) {
145
+ // If the context button is focused then the next focusable item
146
+ // should be the first item in the context menu
147
+ if (document.activeElement === buttonRef.current) {
148
+ event.preventDefault();
149
+ firstLink.focus();
150
+ return;
151
+ }
152
+ // If the last context item is focused then the focus should go to
153
+ // the last breadcrumb item
154
+ if (document.activeElement === lastLink) {
155
+ event.preventDefault();
156
+ lastBreadCrumb.focus();
157
+ }
158
+ }
159
+ // If the user is shift-tabbing...
160
+ if (event.shiftKey) {
161
+ // If the first item is focused then the focus should go back to the context button
162
+ if (event.shiftKey && document.activeElement === firstLink) {
163
+ event.preventDefault();
164
+ (_d = buttonRef === null || buttonRef === void 0 ? void 0 : buttonRef.current) === null || _d === void 0 ? void 0 : _d.focus();
165
+ }
166
+ // If the focus is on the last breadcrumb item then the focus should go
167
+ // to the last item in the context menu
168
+ if (event.shiftKey && document.activeElement === lastBreadCrumb) {
169
+ event.preventDefault();
170
+ lastLink.focus();
171
+ }
172
+ }
173
+ }
174
+ }
175
+ document.addEventListener("keydown", handleKeyDown);
176
+ return () => {
177
+ document.removeEventListener("keydown", handleKeyDown);
178
+ };
179
+ });
180
+ // Separate out the links into first, context and last
181
+ const firstLink = links[0];
182
+ const contextLinks = links.slice(1, links.length - 1);
29
183
  const lastLink = links[links.length - 1];
30
- const menuLinks = links.slice(0, -1);
31
- return (jsx("nav", Object.assign({ className: `${BreadcrumbClasses} ${showContext ? " contextmenu" : ""}` }, { children: jsxs("ol", Object.assign({ className: `${baseClass}--items` }, { children: [jsx("li", Object.assign({ className: `${baseClass}--item home` }, { children: jsx("a", Object.assign({ className: `${baseClass}--link`, href: home.url }, { children: jsx("span", Object.assign({ className: `${baseClass}--link--label` }, { children: home.label })) })) })), jsx("li", Object.assign({ className: `${baseClass}--item--context`, onClick: handleContextMenuToggle }, { children: jsxs("ul", Object.assign({ className: `${baseClass}--items context--menu ${toggleMenuOpen ? " open" : ""}` }, { children: [showContext && jsx(ContextMenu, { links: menuLinks }), !showContext &&
32
- menuLinks.map((link, index) => (jsx("li", Object.assign({ className: `${baseClass}--item` }, { children: jsx("a", Object.assign({ href: link.url, className: `${baseClass}--link` }, { children: jsx("span", Object.assign({ className: `${baseClass}--link--label` }, { children: link.label })) })) }), index)))] })) })), jsx("li", Object.assign({ className: `${baseClass}--item final` }, { children: jsx("a", Object.assign({ className: `${baseClass}--link`, href: lastLink.url }, { children: jsx("span", Object.assign({ className: `${baseClass}--link--label` }, { children: lastLink.label })) })) }))] })) })));
184
+ return (jsxs("nav", Object.assign({ className: breadcrumbClass }, { children: [jsx("div", Object.assign({ className: innerClass }, { children: jsxs("ol", Object.assign({ className: itemsClass, ref: breadcrumbsRef }, { children: [jsx(BreadcrumbItem, { url: firstLink.url, label: firstLink.label, labelShown: false, className: itemFirstClass }), contextLinks && contextLinks.length > 0 ? (jsxs("li", Object.assign({ className: contextItemsClass }, { children: [jsx("ol", Object.assign({ className: itemsClass }, { children: contextLinks.map((contextLink, i) => (jsx(BreadcrumbItem, { url: contextLink.url, label: contextLink.label }, i))) })), jsx("button", { "aria-label": buttonLabel, "aria-expanded": isCtxMenuOpen, "aria-controls": contextId, className: buttonClass, onClick: handleButtonClick, ref: buttonRef })] }))) : null, lastLink && (jsx(BreadcrumbItem, { url: lastLink.url, label: lastLink.label }))] })) })), jsx("div", Object.assign({ className: contextMenuClass, id: contextId, hidden: !isCtxMenuOpen, style: { left: ctxMenuPosition.start, top: ctxMenuPosition.top } }, { children: jsx(ContextMenu, { links: contextLinks, ref: ctxMenuRef }) }))] })));
33
185
  };
34
186
 
35
187
  export { Breadcrumb as default };
@@ -1,18 +1,18 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
+ import React from 'react';
2
3
  import classNames from 'classnames';
3
4
  import useGlobalSettings from '../../hooks/useGlobalSettings.js';
4
5
  import 'tslib';
5
- import 'react';
6
6
  import '../../GlobalCtx-7fb23cfa.js';
7
7
 
8
- const ContextMenu = ({ className, links }) => {
8
+ const ContextMenu = React.forwardRef(({ className, links }, ref) => {
9
9
  const { prefix } = useGlobalSettings();
10
10
  const baseClass = `${prefix}--context-menu`;
11
11
  const contextMenuClasses = classNames(className, {
12
12
  [baseClass]: true,
13
13
  });
14
- return (jsx("ul", Object.assign({ className: contextMenuClasses }, { children: links &&
14
+ return (jsx("ol", Object.assign({ className: contextMenuClasses, ref: ref }, { children: links &&
15
15
  links.map((link, i) => (jsx("li", Object.assign({ className: `${baseClass}--item ${link.endsection ? "endsection" : ""}` }, { children: jsx("a", Object.assign({ href: link.url, className: `${baseClass}--link` }, { children: jsx("span", Object.assign({ className: `${baseClass}--label` }, { children: link.label })) })) }), `${baseClass}--item-${i}`))) })));
16
- };
16
+ });
17
17
 
18
18
  export { ContextMenu as default };
@@ -1,7 +1,7 @@
1
1
  export { default as ContextMenu } from './ContextMenu.js';
2
2
  import 'react/jsx-runtime';
3
+ import 'react';
3
4
  import 'classnames';
4
5
  import '../../hooks/useGlobalSettings.js';
5
6
  import 'tslib';
6
- import 'react';
7
7
  import '../../GlobalCtx-7fb23cfa.js';
@@ -1 +1 @@
1
- @charset "UTF-8";.ilo--accordion--button{display:flex;flex-direction:row;justify-content:space-between;width:100%;margin:0;padding-block:.857449089rem;padding-inline-end:2.3579849946rem;background-color:#fff;background-position:calc(100% - .3215434084rem) 50%;background-repeat:no-repeat;background-size:1.2861736334rem 1.2861736334rem;border:none;border-top:2px solid #edf0f2;fill:#230050;font-family:Overpass,Noto Sans,Noto Sans CJK JP,Yu Gothic,Hiragino Sans,TakaoPGothic,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif;color:#230050;font-weight:500;transition:all .5s ease-in-out;font-size:16px;letter-spacing:-.02em;line-height:21.6px;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23230050' d='M18 11h-5V6h-2v5H6v2h5v5h2v-5h5v-2z'/%3E%3C/svg%3E")}.ilo--accordion--button__large{font-size:18.66px;letter-spacing:-.035em;line-height:24.26px;padding-block:1.0718113612rem;padding-inline-end:2.3579849946rem}.ilo--accordion--button:focus,.ilo--accordion--button:hover{background-color:#ebf5fd;border-top-color:#1e2dbe;color:#1e2dbe;fill:#1e2dbe;cursor:pointer}.ilo--accordion--button:focus[aria-expanded=true],.ilo--accordion--button:hover[aria-expanded=true]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%231E2DBE' d='M18 11H6v2h12v-2z'/%3E%3C/svg%3E")}.ilo--accordion--button:focus[aria-expanded=false],.ilo--accordion--button:hover[aria-expanded=false]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%231E2DBE' d='M18 11h-5V6h-2v5H6v2h5v5h2v-5h5v-2z'/%3E%3C/svg%3E")}@media screen and (min-width:610px){.ilo--accordion--button{font-size:16px;letter-spacing:-.02em;line-height:21.6px}.ilo--accordion--button__large{font-size:18.66px;letter-spacing:-.035em;line-height:24.26px;padding:1.0718113612rem 0;padding-inline-end:2.3579849946rem}}.ilo--accordion--button[aria-expanded=true]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23230050' d='M18 11H6v2h12v-2z'/%3E%3C/svg%3E")}.ilo--accordion--button[aria-expanded=false]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23230050' d='M18 11h-5V6h-2v5H6v2h5v5h2v-5h5v-2z'/%3E%3C/svg%3E")}[dir=rtl] .ilo--accordion--button{background-position:calc(0% + .3215434084rem) 50%}.ilo--accordion--panel{max-height:0;transition:all .5s cubic-bezier(0,1,0,1);overflow:hidden}.ilo--accordion--panel .ilo--accordion--innerpanel{padding-bottom:1.9292604502rem;padding-top:.4287245445rem}.ilo--accordion--panel__open{max-height:100vh;transition:all .5s ease-in-out}.ilo--accordion--panel__open.ilo--accordion--panel__scroll{max-height:300px;overflow-y:auto;padding-right:1.7148981779rem}
1
+ @charset "UTF-8";.ilo--accordion--button{display:flex;flex-direction:row;justify-content:space-between;width:100%;margin:0;padding-block:.857449089rem;padding-inline-end:2.3579849946rem;background-color:#fff;background-position:calc(100% - .3215434084rem) 50%;background-repeat:no-repeat;background-size:1.2861736334rem 1.2861736334rem;border:none;border-top:2px solid #edf0f2;fill:#230050;font-family:Overpass,Noto Sans,Noto Sans CJK JP,Yu Gothic,Hiragino Sans,TakaoPGothic,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif;color:#230050;font-weight:500;transition:all .5s ease-in-out;font-size:16px;letter-spacing:-.02em;line-height:21.6px;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23230050' d='M18 11h-5V6h-2v5H6v2h5v5h2v-5h5v-2z'/%3E%3C/svg%3E")}.ilo--accordion--button__large{font-size:18.66px;letter-spacing:-.035em;line-height:24.26px;padding-block:1.0718113612rem;padding-inline-end:2.3579849946rem}.ilo--accordion--button:focus,.ilo--accordion--button:hover{background-color:#ebf5fd;border-top-color:#1e2dbe;color:#1e2dbe;fill:#1e2dbe;cursor:pointer}.ilo--accordion--button:focus[aria-expanded=true],.ilo--accordion--button:hover[aria-expanded=true]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%231E2DBE' d='M18 11H6v2h12v-2z'/%3E%3C/svg%3E")}.ilo--accordion--button:focus[aria-expanded=false],.ilo--accordion--button:hover[aria-expanded=false]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%231E2DBE' d='M18 11h-5V6h-2v5H6v2h5v5h2v-5h5v-2z'/%3E%3C/svg%3E")}@media screen and (min-width:610px){.ilo--accordion--button{font-size:16px;letter-spacing:-.02em;line-height:21.6px}.ilo--accordion--button__large{font-size:18.66px;letter-spacing:-.035em;line-height:24.26px;padding:1.0718113612rem 0;padding-inline-end:2.3579849946rem}}.ilo--accordion--button[aria-expanded=true]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23230050' d='M18 11H6v2h12v-2z'/%3E%3C/svg%3E")}.ilo--accordion--button[aria-expanded=false]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23230050' d='M18 11h-5V6h-2v5H6v2h5v5h2v-5h5v-2z'/%3E%3C/svg%3E")}[dir=rtl] .ilo--accordion--button{background-position:calc(0% + .3215434084rem) 50%}.ilo--accordion--panel{max-height:0;transition:all .5s cubic-bezier(0,1,0,1);overflow:hidden}.ilo--accordion--panel .ilo--accordion--innerpanel{padding-bottom:1.9292604502rem;padding-top:.4287245445rem;padding-inline-end:1.7148981779rem}.ilo--accordion--panel__open{max-height:100vh;transition:all .5s ease-in-out}.ilo--accordion--panel__open.ilo--accordion--panel__scroll{max-height:300px;overflow-y:auto}