colibri-ui 0.8.97 → 0.8.98

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.
Files changed (56) hide show
  1. package/NavigationMenu/NavigationMenu.d.ts +3 -0
  2. package/NavigationMenu/NavigationMenu.js +170 -0
  3. package/NavigationMenu/NavigationMenu.module.css +67 -0
  4. package/NavigationMenu/components/MoreMenu/MoreMenu.module.css +19 -0
  5. package/NavigationMenu/components/MoreMenu/index.d.ts +16 -0
  6. package/NavigationMenu/components/MoreMenu/index.js +155 -0
  7. package/NavigationMenu/components/NavigationLink/NavigationLink.module.css +59 -0
  8. package/NavigationMenu/components/NavigationLink/index.d.ts +16 -0
  9. package/NavigationMenu/components/NavigationLink/index.js +51 -0
  10. package/NavigationMenu/helpers.d.ts +9 -0
  11. package/NavigationMenu/helpers.js +20 -0
  12. package/NavigationMenu/index.d.ts +2 -0
  13. package/NavigationMenu/index.js +7 -0
  14. package/NavigationMenu/index.props.d.ts +40 -0
  15. package/NavigationMenu/package.json +9 -0
  16. package/NavigationMenu/splitItems.d.ts +30 -0
  17. package/NavigationMenu/splitItems.js +54 -0
  18. package/NavigationMenu/useOverflowSplit.d.ts +25 -0
  19. package/NavigationMenu/useOverflowSplit.js +63 -0
  20. package/Theme/index.d.ts +8 -0
  21. package/Theme/themes/buenos_aires.d.ts +4 -0
  22. package/Theme/themes/buenos_aires.js +4 -0
  23. package/Theme/themes/jaipur.d.ts +4 -0
  24. package/Theme/themes/jaipur.js +4 -0
  25. package/esm/NavigationMenu/NavigationMenu.d.ts +3 -0
  26. package/esm/NavigationMenu/NavigationMenu.js +162 -0
  27. package/esm/NavigationMenu/NavigationMenu.module.css +67 -0
  28. package/esm/NavigationMenu/components/MoreMenu/MoreMenu.module.css +19 -0
  29. package/esm/NavigationMenu/components/MoreMenu/index.d.ts +16 -0
  30. package/esm/NavigationMenu/components/MoreMenu/index.js +147 -0
  31. package/esm/NavigationMenu/components/NavigationLink/NavigationLink.module.css +59 -0
  32. package/esm/NavigationMenu/components/NavigationLink/index.d.ts +16 -0
  33. package/esm/NavigationMenu/components/NavigationLink/index.js +41 -0
  34. package/esm/NavigationMenu/helpers.d.ts +9 -0
  35. package/esm/NavigationMenu/helpers.js +15 -0
  36. package/esm/NavigationMenu/index.d.ts +2 -0
  37. package/esm/NavigationMenu/index.js +1 -0
  38. package/esm/NavigationMenu/index.props.d.ts +40 -0
  39. package/esm/NavigationMenu/splitItems.d.ts +30 -0
  40. package/esm/NavigationMenu/splitItems.js +51 -0
  41. package/esm/NavigationMenu/useOverflowSplit.d.ts +25 -0
  42. package/esm/NavigationMenu/useOverflowSplit.js +61 -0
  43. package/esm/Theme/index.d.ts +8 -0
  44. package/esm/Theme/themes/buenos_aires.d.ts +4 -0
  45. package/esm/Theme/themes/buenos_aires.js +4 -0
  46. package/esm/Theme/themes/jaipur.d.ts +4 -0
  47. package/esm/Theme/themes/jaipur.js +4 -0
  48. package/esm/index.d.ts +2 -0
  49. package/esm/index.js +1 -0
  50. package/esm/theme_ba_variables.css +4 -0
  51. package/esm/theme_jaipur_variables.css +4 -0
  52. package/index.d.ts +2 -0
  53. package/index.js +3 -0
  54. package/package.json +1 -1
  55. package/theme_ba_variables.css +4 -0
  56. package/theme_jaipur_variables.css +4 -0
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import type { NavigationMenuProps } from "./index.props";
3
+ export declare const NavigationMenu: React.ForwardRefExoticComponent<NavigationMenuProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,170 @@
1
+ 'use strict';
2
+
3
+ var tslib = require('tslib');
4
+ var React = require('react');
5
+ var clsx = require('clsx');
6
+ var index$1 = require('./components/MoreMenu/index.js');
7
+ var index = require('./components/NavigationLink/index.js');
8
+ var helpers = require('./helpers.js');
9
+ var useOverflowSplit = require('./useOverflowSplit.js');
10
+ var styles = require('./NavigationMenu.module.css');
11
+
12
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
+
14
+ var React__default = /*#__PURE__*/_interopDefault(React);
15
+ var clsx__default = /*#__PURE__*/_interopDefault(clsx);
16
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
17
+
18
+ const FOCUSABLE = "a[href], button, [tabindex]";
19
+ const getActiveIndex = items => items.findIndex(item => item.active);
20
+ const OverflowMenu = _a => {
21
+ var {
22
+ items,
23
+ pinnedItems = [],
24
+ moreItems = [],
25
+ moreLabel,
26
+ onItemClick,
27
+ renderLink,
28
+ navRef,
29
+ rootClassName
30
+ } = _a,
31
+ props = tslib.__rest(_a, ["items", "pinnedItems", "moreItems", "moreLabel", "onItemClick", "renderLink", "navRef", "rootClassName"]);
32
+ const {
33
+ split,
34
+ containerRef,
35
+ listRef,
36
+ measurerRef,
37
+ moreRef
38
+ } = useOverflowSplit.useOverflowSplit({
39
+ itemCount: items.length,
40
+ pinnedCount: pinnedItems.length,
41
+ activeIndex: getActiveIndex(items),
42
+ forceMore: moreItems.length > 0,
43
+ contentKey: [...items, ...pinnedItems].map(item => item.label).join("\n")
44
+ });
45
+ // After `items` change and before the layout effect remeasures, the old
46
+ // split may point to missing indexes: drop them, new items go to "More".
47
+ const visibleIndexes = split === null || split === void 0 ? void 0 : split.visible.filter(index => index < items.length);
48
+ const visibleItems = visibleIndexes ? visibleIndexes.map(index => items[index]) : items;
49
+ const hiddenItems = visibleIndexes ? [...items.filter((_, index) => !visibleIndexes.includes(index)), ...moreItems] : [];
50
+ const isMoreHidden = hiddenItems.length === 0;
51
+ // "More" disappeared with a focused link inside: focus the same item in the
52
+ // row, or the first row link when it is not there.
53
+ const restoreFocus = item => {
54
+ var _a, _b, _c, _d, _e;
55
+ const rows = Array.from((_b = (_a = listRef.current) === null || _a === void 0 ? void 0 : _a.children) !== null && _b !== void 0 ? _b : []);
56
+ const rowItems = [...visibleItems, ...pinnedItems];
57
+ const row = rows[rowItems.findIndex(rowItem => helpers.isSameItem(rowItem, item))];
58
+ (_e = (_c = row === null || row === void 0 ? void 0 : row.querySelector(FOCUSABLE)) !== null && _c !== void 0 ? _c : (_d = listRef.current) === null || _d === void 0 ? void 0 : _d.querySelector(FOCUSABLE)) === null || _e === void 0 ? void 0 : _e.focus();
59
+ };
60
+ const renderItem = (item, index$1) => (/*#__PURE__*/React__default.default.createElement("li", {
61
+ key: helpers.getItemKey(item, index$1),
62
+ className: styles__default.default.list_item
63
+ }, /*#__PURE__*/React__default.default.createElement(index.NavigationLink, {
64
+ item: item,
65
+ onClick: onItemClick,
66
+ renderLink: renderLink
67
+ })));
68
+ return /*#__PURE__*/React__default.default.createElement("nav", Object.assign({}, props, {
69
+ ref: node => {
70
+ containerRef.current = node;
71
+ helpers.assignRef(navRef, node);
72
+ },
73
+ className: clsx__default.default(rootClassName, styles__default.default.overflow, {
74
+ [styles__default.default.overflow_measuring]: !split
75
+ })
76
+ }), /*#__PURE__*/React__default.default.createElement("ul", {
77
+ ref: listRef,
78
+ className: styles__default.default.list
79
+ }, visibleItems.map(renderItem), pinnedItems.map(renderItem), /*#__PURE__*/React__default.default.createElement("li", {
80
+ ref: moreRef,
81
+ className: clsx__default.default(styles__default.default.list_item, {
82
+ [styles__default.default.more_hidden]: isMoreHidden
83
+ }),
84
+ "aria-hidden": isMoreHidden || undefined
85
+ }, /*#__PURE__*/React__default.default.createElement(index$1.MoreMenu, {
86
+ items: hiddenItems,
87
+ label: moreLabel,
88
+ hidden: isMoreHidden,
89
+ onItemClick: onItemClick,
90
+ renderLink: renderLink,
91
+ onFocusLost: restoreFocus
92
+ }))), /*#__PURE__*/React__default.default.createElement("div", {
93
+ ref: measurerRef,
94
+ className: styles__default.default.measurer,
95
+ "aria-hidden": "true"
96
+ }, [...items, ...pinnedItems].map((item, index$1) => (/*#__PURE__*/React__default.default.createElement("span", {
97
+ key: helpers.getItemKey(item, index$1),
98
+ className: index.getLinkClassName("item", item.active)
99
+ }, /*#__PURE__*/React__default.default.createElement(index.LinkContent, {
100
+ item: item
101
+ }))))));
102
+ };
103
+ const ScrollMenu = _a => {
104
+ var {
105
+ items,
106
+ pinnedItems = [],
107
+ moreItems = [],
108
+ onItemClick,
109
+ renderLink,
110
+ navRef,
111
+ rootClassName
112
+ } = _a,
113
+ props = tslib.__rest(_a, ["items", "pinnedItems", "moreItems", "onItemClick", "renderLink", "navRef", "rootClassName"]);
114
+ const scrollRef = React.useRef(null);
115
+ const activeRef = React.useRef(null);
116
+ const allItems = [...items, ...pinnedItems, ...moreItems];
117
+ const activeIndex = getActiveIndex(allItems);
118
+ // Center the active item when it is not fully visible.
119
+ helpers.useIsomorphicLayoutEffect(() => {
120
+ const container = scrollRef.current;
121
+ const active = activeRef.current;
122
+ if (!container || !active) return;
123
+ const {
124
+ offsetLeft,
125
+ offsetWidth
126
+ } = active;
127
+ const isVisible = offsetLeft >= container.scrollLeft && offsetLeft + offsetWidth <= container.scrollLeft + container.clientWidth;
128
+ if (!isVisible) {
129
+ container.scrollLeft = offsetLeft + offsetWidth / 2 - container.clientWidth / 2;
130
+ }
131
+ }, [activeIndex]);
132
+ return /*#__PURE__*/React__default.default.createElement("nav", Object.assign({}, props, {
133
+ ref: navRef,
134
+ className: clsx__default.default(rootClassName, styles__default.default.scroll)
135
+ }), /*#__PURE__*/React__default.default.createElement("div", {
136
+ ref: scrollRef,
137
+ className: styles__default.default.scroll_area
138
+ }, /*#__PURE__*/React__default.default.createElement("ul", {
139
+ className: styles__default.default.list
140
+ }, allItems.map((item, index$1) => (/*#__PURE__*/React__default.default.createElement("li", {
141
+ key: helpers.getItemKey(item, index$1),
142
+ ref: index$1 === activeIndex ? activeRef : undefined,
143
+ className: styles__default.default.list_item
144
+ }, /*#__PURE__*/React__default.default.createElement(index.NavigationLink, {
145
+ item: item,
146
+ onClick: onItemClick,
147
+ renderLink: renderLink
148
+ })))))));
149
+ };
150
+ const NavigationMenu = /*#__PURE__*/React.forwardRef((_a, ref) => {
151
+ var {
152
+ layout = "overflow",
153
+ size = "m",
154
+ moreLabel = "More",
155
+ className
156
+ } = _a,
157
+ props = tslib.__rest(_a, ["layout", "size", "moreLabel", "className"]);
158
+ const rootClassName = clsx__default.default(styles__default.default.root, styles__default.default[`size_${size}`], className);
159
+ return layout === "scroll" ? (/*#__PURE__*/React__default.default.createElement(ScrollMenu, Object.assign({}, props, {
160
+ navRef: ref,
161
+ rootClassName: rootClassName
162
+ }))) : (/*#__PURE__*/React__default.default.createElement(OverflowMenu, Object.assign({}, props, {
163
+ moreLabel: moreLabel,
164
+ navRef: ref,
165
+ rootClassName: rootClassName
166
+ })));
167
+ });
168
+ NavigationMenu.displayName = "NavigationMenu";
169
+
170
+ exports.NavigationMenu = NavigationMenu;
@@ -0,0 +1,67 @@
1
+ .root {
2
+ font-family: var(--font-family-ui);
3
+ min-inline-size: 0;
4
+ }
5
+ .root.size_m {
6
+ --navigation-item-height: calc(var(--space-8) + var(--space-1));
7
+ --navigation-item-font-size: var(--font-size-text-md);
8
+ --navigation-item-line-height: var(--line-height-text-md);
9
+ --navigation-item-padding: var(--space-4);
10
+ --navigation-menu-gap: var(--space-2);
11
+ }
12
+ .root.size_s {
13
+ --navigation-item-height: var(--space-8);
14
+ --navigation-item-font-size: var(--font-size-text-sm);
15
+ --navigation-item-line-height: var(--line-height-text-sm);
16
+ --navigation-item-padding: var(--space-3);
17
+ --navigation-menu-gap: var(--space-1);
18
+ }
19
+
20
+ .list {
21
+ align-items: center;
22
+ display: flex;
23
+ gap: var(--navigation-menu-gap);
24
+ list-style: none;
25
+ margin: 0;
26
+ padding: 0;
27
+ }
28
+ .list_item {
29
+ display: flex;
30
+ flex-shrink: 0;
31
+ }
32
+
33
+ .overflow {
34
+ display: block;
35
+ position: relative;
36
+ }
37
+ .overflow_measuring {
38
+ overflow: hidden;
39
+ }
40
+
41
+ .more_hidden {
42
+ pointer-events: none;
43
+ position: absolute;
44
+ visibility: hidden;
45
+ }
46
+
47
+ .measurer {
48
+ block-size: 0;
49
+ display: flex;
50
+ inline-size: max-content;
51
+ inset-block-start: 0;
52
+ inset-inline-start: 0;
53
+ overflow: hidden;
54
+ pointer-events: none;
55
+ position: absolute;
56
+ visibility: hidden;
57
+ }
58
+
59
+ .scroll_area {
60
+ overflow: auto hidden;
61
+ overscroll-behavior-x: contain;
62
+ position: relative;
63
+ scrollbar-width: none;
64
+ }
65
+ .scroll_area::-webkit-scrollbar {
66
+ display: none;
67
+ }
@@ -0,0 +1,19 @@
1
+ .root {
2
+ display: contents;
3
+ }
4
+
5
+ .chevron {
6
+ flex-shrink: 0;
7
+ transition: transform 0.15s ease;
8
+ }
9
+ .chevron_open {
10
+ transform: rotate(180deg);
11
+ }
12
+
13
+ .options {
14
+ display: flex;
15
+ flex-direction: column;
16
+ font-family: var(--font-family-ui);
17
+ min-inline-size: calc(var(--space-32) + var(--space-16));
18
+ padding: var(--space-1);
19
+ }
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ import type { ReactNode } from "react";
3
+ import type { NavigationMenuItem, NavigationMenuRenderLink } from "../../index.props";
4
+ interface MoreMenuProps {
5
+ items: NavigationMenuItem[];
6
+ label: ReactNode;
7
+ /** Nothing to show: the trigger stays in the DOM only to be measured. */
8
+ hidden: boolean;
9
+ onItemClick?: (item: NavigationMenuItem) => void;
10
+ renderLink?: NavigationMenuRenderLink;
11
+ /** The menu became hidden while this item had focus inside the dropdown. */
12
+ onFocusLost: (item: NavigationMenuItem) => void;
13
+ }
14
+ /** "More" trigger with a dropdown of the items that do not fit. */
15
+ export declare const MoreMenu: ({ items, label, hidden, onItemClick, renderLink, onFocusLost, }: MoreMenuProps) => React.JSX.Element;
16
+ export {};
@@ -0,0 +1,155 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var clsx = require('clsx');
5
+ var index$1 = require('../../../Dropdown/index.js');
6
+ require('../../../Icons/index.js');
7
+ var helpers = require('../../helpers.js');
8
+ var index = require('../NavigationLink/index.js');
9
+ var styles = require('./MoreMenu.module.css');
10
+ var Chevron = require('../../../Icons/Chevron.js');
11
+
12
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
+
14
+ var React__default = /*#__PURE__*/_interopDefault(React);
15
+ var clsx__default = /*#__PURE__*/_interopDefault(clsx);
16
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
17
+
18
+ const getLinkPosition = event => {
19
+ const links = Array.from(event.currentTarget.querySelectorAll('[role="menuitem"]'));
20
+ return {
21
+ links,
22
+ index: links.indexOf(event.target)
23
+ };
24
+ };
25
+ const MoreOptions = ({
26
+ items,
27
+ onItemClick,
28
+ renderLink,
29
+ rootRef,
30
+ onItemFocus,
31
+ onTabOut
32
+ }) => {
33
+ // The dropdown is portaled to the end of the document: move focus into it,
34
+ // to the current section or to the first link.
35
+ helpers.useIsomorphicLayoutEffect(() => {
36
+ var _a, _b;
37
+ const root = rootRef.current;
38
+ (_b = (_a = root === null || root === void 0 ? void 0 : root.querySelector('[aria-current="page"]')) !== null && _a !== void 0 ? _a : root === null || root === void 0 ? void 0 : root.querySelector('[role="menuitem"]')) === null || _b === void 0 ? void 0 : _b.focus();
39
+ }, [rootRef]);
40
+ // Focus events bubble from the links, whatever `renderLink` renders.
41
+ const handleFocus = event => {
42
+ const {
43
+ index
44
+ } = getLinkPosition(event);
45
+ if (index !== -1) onItemFocus(items[index]);
46
+ };
47
+ // Arrow keys move focus through the links, Home/End go to the first and the
48
+ // last one. Tab leaves through the trigger, so focus continues from it.
49
+ const handleKeyDown = event => {
50
+ var _a;
51
+ if (event.key === "Tab") {
52
+ onTabOut();
53
+ return;
54
+ }
55
+ const {
56
+ links,
57
+ index
58
+ } = getLinkPosition(event);
59
+ const last = links.length - 1;
60
+ const targets = {
61
+ ArrowDown: index === last ? 0 : index + 1,
62
+ ArrowUp: index === 0 ? last : index - 1,
63
+ Home: 0,
64
+ End: last
65
+ };
66
+ if (index === -1 || !(event.key in targets)) return;
67
+ event.preventDefault();
68
+ (_a = links[targets[event.key]]) === null || _a === void 0 ? void 0 : _a.focus();
69
+ };
70
+ return (
71
+ /*#__PURE__*/
72
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions -- events come from the focused links
73
+ React__default.default.createElement("div", {
74
+ ref: rootRef,
75
+ className: styles__default.default.options,
76
+ onKeyDown: handleKeyDown,
77
+ onFocus: handleFocus
78
+ }, items.map((item, index$1) => (/*#__PURE__*/React__default.default.createElement(index.NavigationLink, {
79
+ key: helpers.getItemKey(item, index$1),
80
+ item: item,
81
+ view: "option",
82
+ role: "menuitem",
83
+ onClick: onItemClick,
84
+ renderLink: renderLink
85
+ }))))
86
+ );
87
+ };
88
+ /** "More" trigger with a dropdown of the items that do not fit. */
89
+ const MoreMenu = ({
90
+ items,
91
+ label,
92
+ hidden,
93
+ onItemClick,
94
+ renderLink,
95
+ onFocusLost
96
+ }) => {
97
+ const [isOpen, setOpen] = React.useState(false);
98
+ const rootRef = React.useRef(null);
99
+ const overlayRef = React.useRef(null);
100
+ const focusedItemRef = React.useRef(null);
101
+ // A resize hides "More" and removes the dropdown with the focused link.
102
+ // Layout effect: focus moves before the browser paints the new row.
103
+ helpers.useIsomorphicLayoutEffect(() => {
104
+ if (!hidden) return;
105
+ const item = focusedItemRef.current;
106
+ const isFocusLost = !document.activeElement || document.activeElement === document.body;
107
+ focusedItemRef.current = null;
108
+ setOpen(false);
109
+ if (item && isFocusLost) onFocusLost(item);
110
+ }, [hidden]);
111
+ // Selection, Escape and Tab remove the focused link together with the
112
+ // dropdown: return focus to the trigger.
113
+ const close = () => {
114
+ var _a, _b;
115
+ const trigger = (_a = rootRef.current) === null || _a === void 0 ? void 0 : _a.firstElementChild;
116
+ if (((_b = overlayRef.current) === null || _b === void 0 ? void 0 : _b.contains(document.activeElement)) && trigger instanceof HTMLElement) {
117
+ trigger.focus();
118
+ }
119
+ focusedItemRef.current = null;
120
+ setOpen(false);
121
+ };
122
+ return /*#__PURE__*/React__default.default.createElement("span", {
123
+ ref: rootRef,
124
+ className: styles__default.default.root
125
+ }, /*#__PURE__*/React__default.default.createElement(index$1.Dropdown, {
126
+ trigger: ["click"],
127
+ placement: "bottom-end",
128
+ visible: isOpen,
129
+ disabled: hidden,
130
+ onVisibleChange: visible => visible ? setOpen(true) : close(),
131
+ overlay: /*#__PURE__*/React__default.default.createElement(MoreOptions, {
132
+ items: items,
133
+ onItemClick: onItemClick,
134
+ renderLink: renderLink,
135
+ rootRef: overlayRef,
136
+ onItemFocus: item => {
137
+ focusedItemRef.current = item;
138
+ },
139
+ onTabOut: close
140
+ })
141
+ }, /*#__PURE__*/React__default.default.createElement("button", {
142
+ type: "button",
143
+ className: index.getLinkClassName(),
144
+ tabIndex: hidden ? -1 : undefined
145
+ }, label, /*#__PURE__*/React__default.default.createElement(Chevron.Chevron, {
146
+ width: 16,
147
+ height: 16,
148
+ "aria-hidden": "true",
149
+ className: clsx__default.default(styles__default.default.chevron, {
150
+ [styles__default.default.chevron_open]: isOpen
151
+ })
152
+ }))));
153
+ };
154
+
155
+ exports.MoreMenu = MoreMenu;
@@ -0,0 +1,59 @@
1
+ .item {
2
+ align-items: center;
3
+ background: transparent;
4
+ block-size: var(--navigation-item-height);
5
+ border: none;
6
+ border-radius: var(--radius-full);
7
+ box-sizing: border-box;
8
+ color: var(--component-navigation-item-text);
9
+ cursor: pointer;
10
+ display: inline-flex;
11
+ font: inherit;
12
+ font-size: var(--navigation-item-font-size);
13
+ gap: var(--space-2);
14
+ line-height: var(--navigation-item-line-height);
15
+ padding: 0 var(--navigation-item-padding);
16
+ text-decoration: none;
17
+ transition: background-color 0.15s ease;
18
+ white-space: nowrap;
19
+ }
20
+ .item:hover {
21
+ background: var(--component-navigation-item-bg-hover);
22
+ }
23
+ .item:focus-visible {
24
+ outline: var(--focus-ring-width) solid var(--component-navigation-item-focus-ring);
25
+ outline-offset: calc(var(--focus-ring-width) * -1);
26
+ }
27
+ .item_selected, .item_selected:hover {
28
+ background: var(--component-navigation-item-bg-selected);
29
+ color: var(--component-navigation-item-text-selected);
30
+ }
31
+
32
+ .option {
33
+ align-items: center;
34
+ border-radius: var(--radius-xs);
35
+ color: var(--component-menu-text);
36
+ display: flex;
37
+ font-size: var(--font-size-text-sm);
38
+ gap: var(--space-2);
39
+ line-height: var(--line-height-text-sm);
40
+ outline: none;
41
+ padding: var(--space-2) var(--space-3);
42
+ text-decoration: none;
43
+ }
44
+ .option:hover, .option:focus-visible {
45
+ background: var(--component-menu-item-bg-hover);
46
+ }
47
+ .option_selected, .option_selected:hover, .option_selected:focus-visible {
48
+ background: var(--component-navigation-item-bg-selected);
49
+ color: var(--component-navigation-item-text-selected);
50
+ }
51
+ .option_selected:focus-visible {
52
+ outline: var(--focus-ring-width) solid var(--component-navigation-item-focus-ring);
53
+ outline-offset: calc(var(--focus-ring-width) * -1);
54
+ }
55
+
56
+ .icon {
57
+ display: inline-flex;
58
+ flex-shrink: 0;
59
+ }
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ import type { NavigationMenuItem, NavigationMenuRenderLink } from "../../index.props";
3
+ interface NavigationLinkProps {
4
+ item: NavigationMenuItem;
5
+ /** `item` is a pill in the row, `option` is a line inside "More". */
6
+ view?: "item" | "option";
7
+ role?: "menuitem";
8
+ onClick?: (item: NavigationMenuItem) => void;
9
+ renderLink?: NavigationMenuRenderLink;
10
+ }
11
+ export declare const getLinkClassName: (view?: NavigationLinkProps["view"], active?: boolean) => string;
12
+ export declare const LinkContent: ({ item }: {
13
+ item: NavigationMenuItem;
14
+ }) => React.JSX.Element;
15
+ export declare const NavigationLink: ({ item, view, role, onClick, renderLink, }: NavigationLinkProps) => React.JSX.Element;
16
+ export {};
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var clsx = require('clsx');
5
+ var styles = require('./NavigationLink.module.css');
6
+
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var React__default = /*#__PURE__*/_interopDefault(React);
10
+ var clsx__default = /*#__PURE__*/_interopDefault(clsx);
11
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
12
+
13
+ const getLinkClassName = (view = "item", active) => clsx__default.default(styles__default.default[view], {
14
+ [styles__default.default[`${view}_selected`]]: active
15
+ });
16
+ const LinkContent = ({
17
+ item
18
+ }) => (/*#__PURE__*/React__default.default.createElement(React__default.default.Fragment, null, item.icon && (/*#__PURE__*/React__default.default.createElement("span", {
19
+ className: styles__default.default.icon,
20
+ "aria-hidden": "true"
21
+ }, item.icon)), /*#__PURE__*/React__default.default.createElement("span", null, item.label)));
22
+ const NavigationLink = ({
23
+ item,
24
+ view,
25
+ role,
26
+ onClick,
27
+ renderLink
28
+ }) => {
29
+ const props = {
30
+ item,
31
+ className: getLinkClassName(view, item.active),
32
+ children: /*#__PURE__*/React__default.default.createElement(LinkContent, {
33
+ item: item
34
+ }),
35
+ onClick: onClick ? () => onClick(item) : undefined,
36
+ role,
37
+ "aria-current": item.active ? "page" : undefined
38
+ };
39
+ if (renderLink) return /*#__PURE__*/React__default.default.createElement(React__default.default.Fragment, null, renderLink(props));
40
+ return /*#__PURE__*/React__default.default.createElement("a", {
41
+ href: item.href,
42
+ className: props.className,
43
+ onClick: props.onClick,
44
+ role: role,
45
+ "aria-current": props["aria-current"]
46
+ }, props.children);
47
+ };
48
+
49
+ exports.LinkContent = LinkContent;
50
+ exports.NavigationLink = NavigationLink;
51
+ exports.getLinkClassName = getLinkClassName;
@@ -0,0 +1,9 @@
1
+ import { useLayoutEffect } from "react";
2
+ import type { Ref } from "react";
3
+ import type { NavigationMenuItem } from "./index.props";
4
+ export declare const getItemKey: (item: NavigationMenuItem, index: number) => string;
5
+ /** Items are often recreated on every render: compare them by content. */
6
+ export declare const isSameItem: (a: NavigationMenuItem, b: NavigationMenuItem) => boolean;
7
+ /** `useLayoutEffect` without the server rendering warning. */
8
+ export declare const useIsomorphicLayoutEffect: typeof useLayoutEffect;
9
+ export declare const assignRef: <T>(ref: Ref<T> | undefined, value: T | null) => void;
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+
5
+ const getItemKey = (item, index) => {
6
+ var _a, _b;
7
+ return (_b = (_a = item.id) !== null && _a !== void 0 ? _a : item.href) !== null && _b !== void 0 ? _b : `${item.label}-${index}`;
8
+ };
9
+ /** Items are often recreated on every render: compare them by content. */
10
+ const isSameItem = (a, b) => getItemKey(a, 0) === getItemKey(b, 0);
11
+ /** `useLayoutEffect` without the server rendering warning. */
12
+ const useIsomorphicLayoutEffect = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
13
+ const assignRef = (ref, value) => {
14
+ if (typeof ref === "function") ref(value);else if (ref) ref.current = value;
15
+ };
16
+
17
+ exports.assignRef = assignRef;
18
+ exports.getItemKey = getItemKey;
19
+ exports.isSameItem = isSameItem;
20
+ exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
@@ -0,0 +1,2 @@
1
+ export { NavigationMenu } from "./NavigationMenu";
2
+ export type { NavigationMenuItem, NavigationMenuLayout, NavigationMenuLinkProps, NavigationMenuProps, NavigationMenuRenderLink, NavigationMenuSize, } from "./index.props";
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var NavigationMenu = require('./NavigationMenu.js');
4
+
5
+
6
+
7
+ exports.NavigationMenu = NavigationMenu.NavigationMenu;
@@ -0,0 +1,40 @@
1
+ import type { HTMLAttributes, MouseEventHandler, ReactNode } from "react";
2
+ /** `overflow` keeps items in a row and moves the rest to "More", `scroll` is a horizontal strip. */
3
+ export type NavigationMenuLayout = "overflow" | "scroll";
4
+ export type NavigationMenuSize = "s" | "m";
5
+ export interface NavigationMenuItem {
6
+ /** Unique key. Defaults to `href`, then to `label`. */
7
+ id?: string;
8
+ label: string;
9
+ href?: string;
10
+ /** Current section: highlighted, gets `aria-current="page"` and never moves to "More". */
11
+ active?: boolean;
12
+ /** Icon before the label. */
13
+ icon?: ReactNode;
14
+ }
15
+ export interface NavigationMenuLinkProps {
16
+ item: NavigationMenuItem;
17
+ className: string;
18
+ children: ReactNode;
19
+ onClick?: MouseEventHandler<HTMLElement>;
20
+ tabIndex?: number;
21
+ /** Set for links inside the "More" dropdown. */
22
+ role?: "menuitem";
23
+ "aria-current"?: "page";
24
+ }
25
+ /** Renders an item link, for example a router `Link`. Pass every prop except `item` to the element. */
26
+ export type NavigationMenuRenderLink = (props: NavigationMenuLinkProps) => ReactNode;
27
+ export interface NavigationMenuProps extends Omit<HTMLAttributes<HTMLElement>, "children"> {
28
+ items: NavigationMenuItem[];
29
+ /** Always visible after `items`. */
30
+ pinnedItems?: NavigationMenuItem[];
31
+ /** Always inside "More", after the items that do not fit. In the `scroll` layout they end the strip. */
32
+ moreItems?: NavigationMenuItem[];
33
+ layout?: NavigationMenuLayout;
34
+ size?: NavigationMenuSize;
35
+ moreLabel?: ReactNode;
36
+ /** Called on a click on any item, including the ones inside "More". */
37
+ onItemClick?: (item: NavigationMenuItem) => void;
38
+ /** Custom link. Defaults to `<a href>`. */
39
+ renderLink?: NavigationMenuRenderLink;
40
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "sideEffects": [
3
+ "**/*.css",
4
+ "**/*.scss"
5
+ ],
6
+ "module": "../esm/NavigationMenu/index.js",
7
+ "main": "./index.js",
8
+ "types": "./index.d.ts"
9
+ }