@zanichelli/zanichelli-it-frontend-kit 1.4.0-RC2 → 1.4.0-RC3

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 (117) hide show
  1. package/dist/cjs/index-BLzQAHAI.js +1578 -0
  2. package/dist/cjs/index-BLzQAHAI.js.map +1 -0
  3. package/dist/cjs/index.cjs.js +5 -0
  4. package/dist/cjs/index.cjs.js.map +1 -0
  5. package/dist/cjs/loader.cjs.js +15 -0
  6. package/dist/cjs/loader.cjs.js.map +1 -0
  7. package/dist/cjs/zanichelli-it-frontend-kit.cjs.js +27 -0
  8. package/dist/cjs/zanichelli-it-frontend-kit.cjs.js.map +1 -0
  9. package/dist/cjs/zanit-back-to-top.zanit-menubar.zanit-mobile-menubar.zanit-search-form.entry.cjs.js.map +1 -0
  10. package/dist/cjs/zanit-back-to-top_4.cjs.entry.js +1024 -0
  11. package/dist/cjs/zanit-back-to-top_4.cjs.entry.js.map +1 -0
  12. package/dist/collection/collection-manifest.json +15 -0
  13. package/dist/collection/components/back-to-top/back-to-top.css +16 -0
  14. package/dist/collection/components/back-to-top/back-to-top.js +130 -0
  15. package/dist/collection/components/back-to-top/back-to-top.js.map +1 -0
  16. package/dist/collection/components/menubar/menu/menu.css +95 -0
  17. package/dist/collection/components/menubar/menu/menu.js +38 -0
  18. package/dist/collection/components/menubar/menu/menu.js.map +1 -0
  19. package/dist/collection/components/menubar/menubar.css +170 -0
  20. package/dist/{zanichelli-it-frontend-kit/zanit-menubar.entry.js → collection/components/menubar/menubar.js} +203 -25
  21. package/dist/collection/components/menubar/menubar.js.map +1 -0
  22. package/dist/collection/components/menubar/mobile-menubar/mobile-menubar.css +122 -0
  23. package/{www/build/zanit-mobile-menubar.entry.js → dist/collection/components/menubar/mobile-menubar/mobile-menubar.js} +194 -24
  24. package/dist/collection/components/menubar/mobile-menubar/mobile-menubar.js.map +1 -0
  25. package/dist/collection/components/menubar/search-form/search-form.css +232 -0
  26. package/dist/{zanichelli-it-frontend-kit/zanit-search-form.entry.js → collection/components/menubar/search-form/search-form.js} +174 -110
  27. package/dist/collection/components/menubar/search-form/search-form.js.map +1 -0
  28. package/dist/collection/components/menubar/search-form/suggestions.js +85 -0
  29. package/dist/collection/components/menubar/search-form/suggestions.js.map +1 -0
  30. package/dist/collection/index.js +11 -0
  31. package/dist/collection/index.js.map +1 -0
  32. package/dist/collection/utils/index.js +2 -0
  33. package/dist/collection/utils/index.js.map +1 -0
  34. package/dist/collection/utils/subjects.api.js +25 -0
  35. package/dist/collection/utils/subjects.api.js.map +1 -0
  36. package/dist/collection/utils/utils.js +25 -0
  37. package/dist/collection/utils/utils.js.map +1 -0
  38. package/dist/components/index.js +1335 -0
  39. package/dist/components/index.js.map +1 -0
  40. package/dist/{zanichelli-it-frontend-kit/zanit-mobile-menubar.entry.js → components/p-CmameXB-.js} +87 -12
  41. package/dist/components/p-CmameXB-.js.map +1 -0
  42. package/{www/build/zanit-search-form.entry.js → dist/components/p-DNk0AZSw.js} +92 -13
  43. package/dist/components/p-DNk0AZSw.js.map +1 -0
  44. package/dist/{zanichelli-it-frontend-kit/zanit-back-to-top.entry.js → components/zanit-back-to-top.js} +34 -10
  45. package/dist/components/zanit-back-to-top.js.map +1 -0
  46. package/{www/build/zanit-menubar.entry.js → dist/components/zanit-menubar.js} +57 -12
  47. package/dist/components/zanit-menubar.js.map +1 -0
  48. package/dist/components/zanit-mobile-menubar.js +9 -0
  49. package/dist/components/zanit-mobile-menubar.js.map +1 -0
  50. package/dist/components/zanit-search-form.js +9 -0
  51. package/dist/components/zanit-search-form.js.map +1 -0
  52. package/dist/esm/index-DSdvvVFj.js +1549 -0
  53. package/dist/esm/index-DSdvvVFj.js.map +1 -0
  54. package/dist/esm/index.js +4 -0
  55. package/dist/esm/index.js.map +1 -0
  56. package/dist/esm/loader.js +13 -0
  57. package/dist/esm/loader.js.map +1 -0
  58. package/dist/esm/zanichelli-it-frontend-kit.js +23 -0
  59. package/dist/esm/zanichelli-it-frontend-kit.js.map +1 -0
  60. package/dist/esm/zanit-back-to-top.zanit-menubar.zanit-mobile-menubar.zanit-search-form.entry.js.map +1 -0
  61. package/dist/esm/zanit-back-to-top_4.entry.js +1019 -0
  62. package/dist/esm/zanit-back-to-top_4.entry.js.map +1 -0
  63. package/dist/index.cjs.js +1 -0
  64. package/dist/index.js +1 -0
  65. package/dist/zanichelli-it-frontend-kit/index.esm.js +0 -10
  66. package/dist/zanichelli-it-frontend-kit/index.esm.js.map +1 -1
  67. package/dist/zanichelli-it-frontend-kit/p-0f6b9e37.entry.js +2 -0
  68. package/dist/zanichelli-it-frontend-kit/p-0f6b9e37.entry.js.map +1 -0
  69. package/dist/zanichelli-it-frontend-kit/p-DSdvvVFj.js +3 -0
  70. package/dist/zanichelli-it-frontend-kit/p-DSdvvVFj.js.map +1 -0
  71. package/dist/zanichelli-it-frontend-kit/zanichelli-it-frontend-kit.css +1 -993
  72. package/dist/zanichelli-it-frontend-kit/zanichelli-it-frontend-kit.esm.js +1 -49
  73. package/dist/zanichelli-it-frontend-kit/zanichelli-it-frontend-kit.esm.js.map +1 -1
  74. package/dist/zanichelli-it-frontend-kit/zanit-back-to-top.zanit-menubar.zanit-mobile-menubar.zanit-search-form.entry.esm.js.map +1 -0
  75. package/package.json +1 -1
  76. package/www/build/index.esm.js +0 -10
  77. package/www/build/index.esm.js.map +1 -1
  78. package/www/build/p-0f6b9e37.entry.js +2 -0
  79. package/www/build/p-0f6b9e37.entry.js.map +1 -0
  80. package/www/build/p-43c395dc.css +1 -0
  81. package/www/build/p-9134b2b7.js +2 -0
  82. package/www/build/p-DSdvvVFj.js +3 -0
  83. package/www/build/p-DSdvvVFj.js.map +1 -0
  84. package/www/build/zanichelli-it-frontend-kit.css +1 -993
  85. package/www/build/zanichelli-it-frontend-kit.esm.js +1 -49
  86. package/www/build/zanichelli-it-frontend-kit.esm.js.map +1 -1
  87. package/www/build/zanit-back-to-top.zanit-menubar.zanit-mobile-menubar.zanit-search-form.entry.esm.js.map +1 -0
  88. package/www/index.html +2 -11
  89. package/dist/zanichelli-it-frontend-kit/index-DPw_TAEB.js +0 -4170
  90. package/dist/zanichelli-it-frontend-kit/index-DPw_TAEB.js.map +0 -1
  91. package/dist/zanichelli-it-frontend-kit/menu-BsP3cOc_.js +0 -46
  92. package/dist/zanichelli-it-frontend-kit/menu-BsP3cOc_.js.map +0 -1
  93. package/dist/zanichelli-it-frontend-kit/subjects.api-BRhngFD0.js +0 -54
  94. package/dist/zanichelli-it-frontend-kit/subjects.api-BRhngFD0.js.map +0 -1
  95. package/dist/zanichelli-it-frontend-kit/zanit-back-to-top.entry.esm.js.map +0 -1
  96. package/dist/zanichelli-it-frontend-kit/zanit-back-to-top.entry.js.map +0 -1
  97. package/dist/zanichelli-it-frontend-kit/zanit-menubar.entry.esm.js.map +0 -1
  98. package/dist/zanichelli-it-frontend-kit/zanit-menubar.entry.js.map +0 -1
  99. package/dist/zanichelli-it-frontend-kit/zanit-mobile-menubar.entry.esm.js.map +0 -1
  100. package/dist/zanichelli-it-frontend-kit/zanit-mobile-menubar.entry.js.map +0 -1
  101. package/dist/zanichelli-it-frontend-kit/zanit-search-form.entry.esm.js.map +0 -1
  102. package/dist/zanichelli-it-frontend-kit/zanit-search-form.entry.js.map +0 -1
  103. package/www/build/index-DPw_TAEB.js +0 -4170
  104. package/www/build/index-DPw_TAEB.js.map +0 -1
  105. package/www/build/menu-BsP3cOc_.js +0 -46
  106. package/www/build/menu-BsP3cOc_.js.map +0 -1
  107. package/www/build/subjects.api-BRhngFD0.js +0 -54
  108. package/www/build/subjects.api-BRhngFD0.js.map +0 -1
  109. package/www/build/zanit-back-to-top.entry.esm.js.map +0 -1
  110. package/www/build/zanit-back-to-top.entry.js +0 -64
  111. package/www/build/zanit-back-to-top.entry.js.map +0 -1
  112. package/www/build/zanit-menubar.entry.esm.js.map +0 -1
  113. package/www/build/zanit-menubar.entry.js.map +0 -1
  114. package/www/build/zanit-mobile-menubar.entry.esm.js.map +0 -1
  115. package/www/build/zanit-mobile-menubar.entry.js.map +0 -1
  116. package/www/build/zanit-search-form.entry.esm.js.map +0 -1
  117. package/www/build/zanit-search-form.entry.js.map +0 -1
@@ -0,0 +1,1024 @@
1
+ 'use strict';
2
+
3
+ var index = require('./index-BLzQAHAI.js');
4
+
5
+ const backToTopCss = ".sc-zanit-back-to-top-h{opacity:1;transition:all 0.2s linear;visibility:visible}.hidden.sc-zanit-back-to-top-h{opacity:0;visibility:hidden}.sc-zanit-back-to-top-h .z-fab.sc-zanit-back-to-top{--color-primary01:var(--gray950);outline:1px solid var(--color-white)}";
6
+
7
+ const ZanitBackTop = class {
8
+ constructor(hostRef) {
9
+ index.registerInstance(this, hostRef);
10
+ }
11
+ resizeObserver;
12
+ mutationObserver;
13
+ get host() { return index.getElement(this); }
14
+ /** Indicates whether the back-to-top button is visible and usable. */
15
+ showFab = false;
16
+ /** Indicates the current height of the page. */
17
+ currentPageHeight;
18
+ /** Indicates if the viewport is mobile. */
19
+ isMobile = false;
20
+ /** Min page height from which the back-to-top button must appear. */
21
+ pageMinHeight = 1600;
22
+ /** Min scroll height from which the back-to-top button must appear. */
23
+ scrollMinHeight = 800;
24
+ handleScroll() {
25
+ this.updateFabVisibility();
26
+ }
27
+ handleResize = () => {
28
+ const newHeight = document.body.scrollHeight;
29
+ if (newHeight !== this.currentPageHeight) {
30
+ this.currentPageHeight = newHeight;
31
+ this.updateFabVisibility();
32
+ }
33
+ };
34
+ connectedCallback() {
35
+ this.currentPageHeight = document.body.scrollHeight;
36
+ this.resizeObserver = new ResizeObserver(this.handleResize);
37
+ this.resizeObserver.observe(document.body);
38
+ this.mutationObserver = new MutationObserver(this.handleResize);
39
+ this.mutationObserver.observe(document.body, { childList: true, subtree: true });
40
+ this.updateFabVisibility();
41
+ const mobileMediaQuery = window.matchMedia('(width < 768px)');
42
+ this.isMobile = mobileMediaQuery.matches;
43
+ mobileMediaQuery.onchange = (mql) => {
44
+ this.isMobile = mql.matches;
45
+ };
46
+ }
47
+ disconnectedCallback() {
48
+ this.resizeObserver.disconnect();
49
+ this.mutationObserver.disconnect();
50
+ }
51
+ updateFabVisibility() {
52
+ this.showFab = this.currentPageHeight > this.pageMinHeight && window.scrollY > this.scrollMinHeight;
53
+ }
54
+ scroll() {
55
+ window.scrollTo({ top: 0, behavior: 'smooth' });
56
+ }
57
+ render() {
58
+ return (index.h(index.Host, { key: '3abd8ab70075d8042285d8ed6fab0729c4b5ec42', class: { hidden: !this.showFab }, "aria-hidden": this.showFab ? 'false' : 'true' }, index.h("button", { key: '11b149d5f1d75720d4bdd329e9897da782092ed1', class: { 'z-fab': true, 'z-fab-extended': !this.isMobile }, onClick: () => this.scroll() }, index.h("z-icon", { key: 'fbdff1419da6d20ab594ffd11c58d7bf25fb7aa6', name: "back-top" }), index.h("span", { key: '0292f761b5459c372f003ed514abbd3403570c75' }, "Torna su"))));
59
+ }
60
+ };
61
+ ZanitBackTop.style = backToTopCss;
62
+
63
+ /**
64
+ * Check if an element contains an event target by checking its composedPath.
65
+ * Useful when an event target may come from a component's shadow DOM.
66
+ */
67
+ const containsTarget = (ancestor, event) => {
68
+ return event
69
+ .composedPath()
70
+ .filter((el) => el !== document && el !== window.window)
71
+ .some((el) => ancestor.contains(el));
72
+ };
73
+ /** Move the focus to `next` element, set tabindex to 0 for `next` and -1 to `current`. */
74
+ const moveFocus = (current, next) => {
75
+ current.tabIndex = -1;
76
+ next.tabIndex = 0;
77
+ next.focus({ preventScroll: true });
78
+ };
79
+ /** Check if event key is ArrowUp */
80
+ const isArrowUpKey = (event) => event.key === 'ArrowUp';
81
+ /** Check if event key is ArrowDown */
82
+ const isArrowDownKey = (event) => event.key === 'ArrowDown';
83
+ /** Check if event key is Tab */
84
+ const isTabKey = (event) => event.key === 'Tab';
85
+ /** Check if event key is Escape */
86
+ const isEscKey = (event) => event.key === 'Escape';
87
+
88
+ const DEFAULT_GROUP_KEY = 'default';
89
+ const DEFAULT_GROUP = {
90
+ id: DEFAULT_GROUP_KEY,
91
+ label: DEFAULT_GROUP_KEY,
92
+ };
93
+ /** Get the items grouped by their group. */
94
+ const getGroupedItems = (items) => {
95
+ const groups = items.reduce((grouped, item) => {
96
+ const itemGroup = grouped.find(({ group }) => group.id === (item.group?.id || DEFAULT_GROUP.id));
97
+ if (!itemGroup) {
98
+ grouped.push({ group: item.group ?? DEFAULT_GROUP, items: [item] });
99
+ }
100
+ else {
101
+ itemGroup.items.push(item);
102
+ }
103
+ return grouped;
104
+ }, []);
105
+ // Sort to keep default group at the end
106
+ return groups.sort((a, b) => (a.group.id === DEFAULT_GROUP_KEY ? 1 : b.group.id === DEFAULT_GROUP_KEY ? -1 : 0));
107
+ };
108
+ /**
109
+ * Floating menu component. It shows a list of items that can be grouped.
110
+ */
111
+ const Menu = ({ controlledBy, items, currentPath = [], onItemKeyDown }) => {
112
+ if (!items?.length) {
113
+ return null;
114
+ }
115
+ const groups = getGroupedItems(items);
116
+ const isActive = (item) => currentPath.includes(controlledBy) && currentPath.includes(item.id);
117
+ return (index.h("div", { class: "menu-wrapper", role: "none" },
118
+ index.h("div", { class: "menu", "aria-labelledby": controlledBy ?? undefined, role: "menu" }, groups.map(({ group, items }) => (index.h("div", { class: { group: true, highlight: items.some((item) => item.highlight) } },
119
+ group.id !== DEFAULT_GROUP_KEY ? (index.h("div", { class: "group-name", id: group.id }, group.label)) : groups.length > 1 ? (
120
+ // empty div to keep the same height as the other groups
121
+ index.h("div", { class: "group-name" })) : null,
122
+ index.h("ul", { class: "menu-list", role: "group", "aria-labelledby": group.id !== DEFAULT_GROUP_KEY ? group.id : undefined }, items.map((item) => (index.h("li", { role: "none" }, item.href && (index.h("a", { class: {
123
+ 'menu-item': true,
124
+ 'active': isActive(item),
125
+ }, href: item.href, role: "menuitem", tabIndex: -1, "aria-current": isActive(item) ? 'page' : 'false', onKeyDown: (event) => onItemKeyDown(event), target: item.target }, item.label))))))))))));
126
+ };
127
+
128
+ var SearchEnv;
129
+ (function (SearchEnv) {
130
+ SearchEnv["DEV"] = "dev";
131
+ SearchEnv["TEST"] = "test";
132
+ SearchEnv["PROD"] = "prod";
133
+ })(SearchEnv || (SearchEnv = {}));
134
+ const S3_SHOP_URL = {
135
+ dev: 'https://zanichelli-shop-dev.s3.eu-west-1.amazonaws.com',
136
+ test: 'https://zanichelli-shop-test.s3.eu-west-1.amazonaws.com',
137
+ prod: 'https://zanichelli-shop.s3.eu-west-1.amazonaws.com',
138
+ };
139
+ async function getSubjectsByArea(searchEnv) {
140
+ try {
141
+ const response = await fetch(`${S3_SHOP_URL[searchEnv]}/categories.json`);
142
+ if (!response.ok) {
143
+ throw new Error(`HTTP ${response.status}`);
144
+ }
145
+ return await response.json();
146
+ }
147
+ catch (err) {
148
+ console.error('Error fetching subjects:', err);
149
+ return {};
150
+ }
151
+ }
152
+
153
+ const menubarCss = ":host{position:relative;z-index:2;display:flex;width:100%;background-color:#fff;color:var(--gray900);font-family:var(--font-family-sans)}:host,*,::before,::after{box-sizing:border-box}*:focus:focus-visible{box-shadow:var(--shadow-focus-primary);outline:none}ul{padding:0;margin:0;list-style:none}a{color:var(--gray900);cursor:pointer;text-decoration:none}button{all:unset;cursor:pointer}:host nav{width:100%}.shadow-wrapper{position:relative;z-index:1;display:flex;width:100%}.shadow-wrapper::after{position:absolute;top:0;right:0;width:100%;height:100%;background:transparent;box-shadow:var(--shadow-1);content:'';pointer-events:none}.width-limiter{position:relative;display:flex;width:100%;max-width:var(--zanit-menubar-max-width, 1366px);margin:0 auto}.shadow-wrapper+.shadow-wrapper{z-index:0}.sub-menubar>ul{gap:28px}.width-limiter>ul,.sub-menubar>ul{position:relative;z-index:3;display:flex;width:100%;align-items:center;padding:0 var(--grid-margin);margin-right:auto;margin-left:auto;gap:20px}.shadow-wrapper>.width-limiter,.shadow-wrapper>ul{width:100%;max-width:var(--zanit-menubar-max-width, 1366px)}ul.menubar{height:3rem;padding-right:0}.menubar z-ghost-loading{display:block;width:120px;height:1.25rem}.menubar>li[role='separator']{width:1px;height:1.25rem;background-color:#000}.menubar-item{position:relative;display:flex;align-items:center;padding:14px 0;font-size:1rem;gap:8px;line-height:1.25rem}.menubar .menubar-item{text-transform:uppercase}.menubar-item [data-text]{display:flex;flex-direction:column}.menubar-item.active>[data-text],.menubar-item:hover>[data-text],.menubar-item:focus:focus-visible>[data-text]{font-weight:var(--font-bd)}.menubar-item>[data-text]::after{height:0;content:attr(data-text) / '';font-weight:var(--font-bd);letter-spacing:normal;pointer-events:none;user-select:none;visibility:hidden}.sub-menubar .menubar-item.active::after{position:absolute;z-index:-1;bottom:0;left:-4px;width:calc(100% + 8px);height:4px;background-color:var(--red500);content:''}zanit-search-form{margin-left:auto}";
154
+
155
+ const menuCss$1 = ".menu-wrapper{width:100%;background-color:#fff}.menu{position:relative;display:flex;width:100%;flex-direction:column;gap:32px 0}.menu .group{display:flex;flex-direction:column}.menu .group .group-name{border-bottom:1px solid currentcolor;margin-bottom:4px;color:var(--red500);font-size:0.875rem;font-weight:var(--font-md)}.menu .group .menu-list{display:flex;flex-direction:column;gap:8px}.menu .group .menu-list .menu-item{display:block;border-bottom:2px solid transparent;font-size:0.875rem;font-weight:var(--font-md)}.menu .menu-list .menu-item.active,.menu .menu-list .menu-item:hover{border-bottom-color:var(--red500)}.menu .group.highlight .menu-list .menu-item{font-size:1rem}@media (width >= 768px){.menu-wrapper{position:absolute;top:100%;left:0;display:flex;justify-content:center;box-shadow:var(--shadow-1)}.menu{display:grid;width:100%;max-width:var(--zanit-menubar-max-width, 1366px);padding:16px var(--grid-margin);gap:0 24px;grid-auto-columns:minmax(0, max-content);grid-auto-flow:column;grid-template-rows:minmax(0, max-content) max-content}.menu .group{display:grid;grid-row:1 / -1;grid-template-columns:1fr;grid-template-rows:subgrid}@supports not (grid-template-rows: subgrid){.menu .group{grid-template-rows:repeat(auto-fit, minmax(0, max-content))}}.menu .group .group-name{border:none;margin-bottom:16px}.menu .group .menu-list .menu-item{font-size:1rem}.menu .group.highlight .menu-list .menu-item{font-size:1.5rem}}";
156
+
157
+ const ZanitMenubar = class {
158
+ constructor(hostRef) {
159
+ index.registerInstance(this, hostRef);
160
+ }
161
+ get host() { return index.getElement(this); }
162
+ /** Menubar items extracted from `data`. */
163
+ items = [];
164
+ /** ID of the currently open menu. */
165
+ openMenu = undefined;
166
+ /** ID of the item to show the subitems navbar for. */
167
+ openNavbar = undefined;
168
+ /** IDs of the current prop */
169
+ currentPath = [];
170
+ isMobile = false;
171
+ loading = false;
172
+ /** The data to build the menu (as an array of `MenubarItem` or a JSON array) or the url to fetch to retrieve it. */
173
+ data;
174
+ /** Path of the current item. */
175
+ current = undefined;
176
+ /** Initial search query. */
177
+ searchQuery = undefined;
178
+ /** Environment for search suggestions */
179
+ searchEnv = SearchEnv.PROD;
180
+ /** Search area (e.g. "SCUOLA", "UNIVERSITÀ", "DIZIONARI"). */
181
+ searchArea;
182
+ timerId;
183
+ /** Setup the list of items. */
184
+ async parseData(data) {
185
+ if (!data) {
186
+ return;
187
+ }
188
+ if (data instanceof URL) {
189
+ this.items = await this.fetchData(data);
190
+ }
191
+ else if (data instanceof Promise) {
192
+ this.loading = true;
193
+ this.items = await data;
194
+ this.loading = false;
195
+ }
196
+ else if (typeof data === 'string') {
197
+ try {
198
+ this.items = JSON.parse(data);
199
+ if (!Array.isArray(this.items) || !this.items?.every((item) => item)) {
200
+ throw new Error('Expected an array of MenubarItem objects.');
201
+ }
202
+ }
203
+ catch {
204
+ let url;
205
+ try {
206
+ url = new URL(data);
207
+ }
208
+ catch {
209
+ throw new Error('Invalid string provided for `data` property: not a valid url or JSON.');
210
+ }
211
+ this.items = await this.fetchData(url);
212
+ }
213
+ }
214
+ else if (Array.isArray(data) && data.every((item) => item)) {
215
+ this.items = data;
216
+ }
217
+ else {
218
+ throw new Error('Invalid `data` property value. Expected an url, a JSON or an array/promise of MenubarItem objects.');
219
+ }
220
+ }
221
+ onItemsChange() {
222
+ this.initTabindex();
223
+ }
224
+ onCurrentChange() {
225
+ this.currentPath = this.current?.split('/').filter(Boolean) || [];
226
+ }
227
+ async connectedCallback() {
228
+ const mobileMediaQuery = window.matchMedia('(width < 768px)');
229
+ this.isMobile = mobileMediaQuery.matches;
230
+ mobileMediaQuery.onchange = (mql) => {
231
+ this.isMobile = mql.matches;
232
+ this.initTabindex();
233
+ this.openMenu = undefined;
234
+ };
235
+ await this.parseData(this.data);
236
+ this.onCurrentChange();
237
+ this.initTabindex();
238
+ }
239
+ /** Close any open menu when clicking outside. */
240
+ handleOutsideClick(event) {
241
+ if (!this.openMenu || containsTarget(this.host, event)) {
242
+ return;
243
+ }
244
+ this.openMenu = undefined;
245
+ }
246
+ /** Close any open menu when pressing Escape or Tab.
247
+ * Uses document-level listener to ensure Escape works from any focus location within the menu.
248
+ */
249
+ handleKeydown(event) {
250
+ switch (event.key) {
251
+ case 'Escape': {
252
+ if (this.openMenu) {
253
+ event.preventDefault();
254
+ // Return focus to the menu trigger after closing
255
+ const menuTriggerId = this.openMenu;
256
+ this.openMenu = undefined;
257
+ // Use setTimeout(0) to defer focus until after Stencil's render cycle completes
258
+ setTimeout(() => {
259
+ const menuTrigger = this.host.shadowRoot.getElementById(menuTriggerId);
260
+ if (menuTrigger) {
261
+ menuTrigger.focus();
262
+ }
263
+ }, 0);
264
+ }
265
+ break;
266
+ }
267
+ case 'Tab':
268
+ this.openMenu = undefined;
269
+ break;
270
+ }
271
+ }
272
+ handleMouseover() {
273
+ clearTimeout(this.timerId);
274
+ }
275
+ /**
276
+ * Automatically close any open menu on mouseout after with a little delay.
277
+ * The delay is useful to avoid immediate closing when the pointer briefly leaves the component.
278
+ */
279
+ handleMouseout(event) {
280
+ this.timerId = window.setTimeout(() => {
281
+ if (!this.openMenu || containsTarget(this.host, event)) {
282
+ return;
283
+ }
284
+ this.openMenu = undefined;
285
+ }, 500);
286
+ }
287
+ /** Close the menu when it loses focus. */
288
+ handleFocusout(event) {
289
+ const relatedTarget = event.relatedTarget;
290
+ if (!this.openMenu || this.host.shadowRoot.querySelector('.menu')?.contains(relatedTarget)) {
291
+ return;
292
+ }
293
+ this.openMenu = undefined;
294
+ }
295
+ /** Fetch data from passed URL. */
296
+ async fetchData(url) {
297
+ try {
298
+ this.loading = true;
299
+ const data = await (await fetch(url)).json();
300
+ this.loading = false;
301
+ if (!Array.isArray(data) || !data.every((item) => item)) {
302
+ throw new Error('Invalid data structure. Expected an array of MenuItem objects.');
303
+ }
304
+ return data;
305
+ }
306
+ catch (error) {
307
+ this.loading = false;
308
+ console.error('Error fetching menubar data:', error);
309
+ throw new Error('Failed to fetch menubar data from the provided URL.', { cause: error });
310
+ }
311
+ }
312
+ /** Initialize tabindex on menuitems of menubars, setting -1 to all but the first one. */
313
+ initTabindex() {
314
+ setTimeout(() => {
315
+ this.host.shadowRoot.querySelectorAll('[role="menubar"]')?.forEach((menubar) => {
316
+ menubar
317
+ .querySelectorAll('[role="menuitem"]')
318
+ ?.forEach((item, index) => item.setAttribute('tabindex', index === 0 ? '0' : '-1'));
319
+ });
320
+ }, 100);
321
+ }
322
+ /** Indicates whether the element has to be highlighted by checking whether it is set as current or one of its descendants is. */
323
+ isActive(item) {
324
+ if (this.currentPath.length === 0) {
325
+ return false;
326
+ }
327
+ if (this.currentPath.includes(item.id)) {
328
+ return true;
329
+ }
330
+ if (item.menuItems?.length) {
331
+ return item.menuItems.some((menuItem) => menuItem.id === this.current);
332
+ }
333
+ if (item.navbarItems?.length) {
334
+ const isActive = item.navbarItems.some((navbarItem) => this.isActive(navbarItem));
335
+ if (isActive) {
336
+ return true;
337
+ }
338
+ }
339
+ return false;
340
+ }
341
+ /** Opens the menu associated with the menubar `item`, if any. */
342
+ showMenu(item) {
343
+ this.openMenu = undefined; // close any open menu first
344
+ if (!item.menuItems?.length) {
345
+ return;
346
+ }
347
+ this.openMenu = item.id;
348
+ }
349
+ /** Get all elements with `menuitem` role inside parent's `menubar`. * */
350
+ getParentMenubarElements(itemEl) {
351
+ return Array.from(itemEl?.closest('[role="menubar"]')?.querySelectorAll(':scope > li a[role="menuitem"]') ?? []);
352
+ }
353
+ /** Move the focus to the previous menubar item, or the last one. Then open its menu if any other menu was open. */
354
+ focusPreviousItem(itemEl) {
355
+ this.openMenu = undefined; // close any open menu first
356
+ const menubarElements = this.getParentMenubarElements(itemEl);
357
+ const currentIndex = menubarElements.indexOf(itemEl);
358
+ const prevItem = menubarElements[(currentIndex - 1 + menubarElements.length) % menubarElements.length]; // get previous item or last one
359
+ moveFocus(itemEl, prevItem);
360
+ // open the item's menu if any other menu was open
361
+ if (prevItem.ariaHasPopup === 'true' && this.openMenu) {
362
+ this.openMenu = prevItem.id;
363
+ }
364
+ }
365
+ /** Move the focus to the next menubar item, or the first one. Then open its menu if any other menu was open. */
366
+ focusNextItem(itemEl) {
367
+ this.openMenu = undefined; // close any open menu first
368
+ const menubarElements = this.getParentMenubarElements(itemEl);
369
+ const currentIndex = menubarElements.indexOf(itemEl);
370
+ const nextItem = menubarElements[(currentIndex + 1) % menubarElements.length]; // get next item or first one
371
+ moveFocus(itemEl, nextItem);
372
+ // open the item's menu if any other menu was open
373
+ if (nextItem.ariaHasPopup === 'true' && this.openMenu) {
374
+ this.openMenu = nextItem.id;
375
+ }
376
+ }
377
+ /** Handles keyboard navigation on menubar items. */
378
+ handleItemKeydown(event, item) {
379
+ const target = event.target;
380
+ switch (event.key) {
381
+ case 'Home': {
382
+ event.preventDefault();
383
+ event.stopPropagation();
384
+ const firstItem = this.getParentMenubarElements(target)[0];
385
+ moveFocus(target, firstItem);
386
+ break;
387
+ }
388
+ case 'End': {
389
+ event.preventDefault();
390
+ event.stopPropagation();
391
+ const lastItem = this.getParentMenubarElements(target).pop();
392
+ moveFocus(target, lastItem);
393
+ break;
394
+ }
395
+ case 'ArrowUp': {
396
+ if (!item.menuItems?.length) {
397
+ break;
398
+ }
399
+ event.preventDefault();
400
+ event.stopPropagation();
401
+ this.openMenu = item.id;
402
+ // focus last item of the menu
403
+ setTimeout(() => {
404
+ const menuItems = Array.from(this.host.shadowRoot.querySelectorAll(`[aria-labelledby=${item.id}] [role="menuitem"]`));
405
+ moveFocus(target, menuItems[menuItems.length - 1]);
406
+ }, 100);
407
+ break;
408
+ }
409
+ case 'ArrowRight': {
410
+ event.preventDefault();
411
+ event.stopPropagation();
412
+ this.focusNextItem(target);
413
+ break;
414
+ }
415
+ case 'ArrowDown': {
416
+ if (!item.menuItems?.length) {
417
+ break;
418
+ }
419
+ this.openItemMenu(item);
420
+ break;
421
+ }
422
+ case 'ArrowLeft': {
423
+ event.preventDefault();
424
+ event.stopPropagation();
425
+ this.focusPreviousItem(target);
426
+ break;
427
+ }
428
+ case ' ': {
429
+ event.preventDefault();
430
+ event.stopPropagation();
431
+ if (this.openMenu === item.id) {
432
+ this.openMenu = undefined;
433
+ break;
434
+ }
435
+ else if (item.menuItems?.length) {
436
+ this.openItemMenu(item);
437
+ break;
438
+ }
439
+ }
440
+ }
441
+ }
442
+ /** Get the previous element with `role=group`. */
443
+ getPreviousGroup(groupContainer) {
444
+ const groups = Array.from(groupContainer?.closest('[role="menu"]')?.querySelectorAll('[role="group"]') ?? []);
445
+ const currentIndex = groups.indexOf(groupContainer);
446
+ return groups[currentIndex - 1];
447
+ }
448
+ /** Get the next element with `role=group`. */
449
+ getNextGroup(groupContainer) {
450
+ const groups = Array.from(groupContainer?.closest('[role="menu"]')?.querySelectorAll('[role="group"]') ?? []);
451
+ const currentIndex = groups.indexOf(groupContainer);
452
+ return groups[currentIndex + 1];
453
+ }
454
+ openItemMenu(item) {
455
+ this.openMenu = item.id;
456
+ setTimeout(() => {
457
+ // focus first item of the menu
458
+ const firstMenuItem = this.host.shadowRoot.querySelector(`[aria-labelledby=${item.id}] [role="menuitem"]`);
459
+ firstMenuItem.tabIndex = 0;
460
+ firstMenuItem.focus({ preventScroll: true });
461
+ }, 100);
462
+ }
463
+ /** Handles keyboard navigation events from `Menu` component. */
464
+ handleMenuKeydown(event) {
465
+ const itemElement = event.target;
466
+ const items = Array.from(itemElement.closest('[role="menu"]')?.querySelectorAll('[role="menuitem"]') ?? []);
467
+ const currentIndex = items.indexOf(itemElement);
468
+ switch (event.key) {
469
+ case 'ArrowUp': {
470
+ event.preventDefault();
471
+ event.stopPropagation();
472
+ const prevItem = items[currentIndex - 1] || items[items.length - 1];
473
+ moveFocus(itemElement, prevItem);
474
+ break;
475
+ }
476
+ // Move the focus to the first item of the next group if any, otherwise move it to the next menubar item
477
+ case 'ArrowRight': {
478
+ event.preventDefault();
479
+ event.stopPropagation();
480
+ const currentGroup = itemElement.closest('[role=group]');
481
+ const nextGroup = this.getNextGroup(currentGroup);
482
+ if (!nextGroup) {
483
+ itemElement.tabIndex = -1;
484
+ const menuTriggerId = itemElement.closest('[role="menu"][aria-labelledby]').getAttribute('aria-labelledby');
485
+ const focusedItem = this.host.shadowRoot.getElementById(menuTriggerId);
486
+ this.focusNextItem(focusedItem);
487
+ break;
488
+ }
489
+ const nextGroupItems = (nextGroup.querySelectorAll('[role="menuitem"]') ?? []);
490
+ moveFocus(itemElement, nextGroupItems[0]);
491
+ break;
492
+ }
493
+ case 'ArrowDown': {
494
+ event.preventDefault();
495
+ event.stopPropagation();
496
+ const nextItem = items[currentIndex + 1] || items[0];
497
+ moveFocus(itemElement, nextItem);
498
+ break;
499
+ }
500
+ // Move the focus to the first item of the previous group if any, otherwise move it to the previous menubar item
501
+ case 'ArrowLeft': {
502
+ event.preventDefault();
503
+ event.stopPropagation();
504
+ const currentGroup = itemElement.closest('[role=group]');
505
+ const prevGroup = this.getPreviousGroup(currentGroup);
506
+ if (!prevGroup) {
507
+ itemElement.tabIndex = -1;
508
+ const menuTriggerId = itemElement.closest('[role="menu"][aria-labelledby]').getAttribute('aria-labelledby');
509
+ const focusedItem = this.host.shadowRoot.getElementById(menuTriggerId);
510
+ this.focusPreviousItem(focusedItem);
511
+ break;
512
+ }
513
+ const prevGroupItems = (prevGroup.querySelectorAll('[role="menuitem"]') ?? []);
514
+ moveFocus(itemElement, prevGroupItems[0]);
515
+ break;
516
+ }
517
+ case 'Home':
518
+ // Move to the first menu item
519
+ event.preventDefault();
520
+ event.stopPropagation();
521
+ moveFocus(itemElement, items[0]);
522
+ break;
523
+ case 'End':
524
+ // Move to the last menu item
525
+ event.preventDefault();
526
+ event.stopPropagation();
527
+ moveFocus(itemElement, items[items.length - 1]);
528
+ break;
529
+ }
530
+ }
531
+ render() {
532
+ if (this.isMobile) {
533
+ return (index.h("zanit-mobile-menubar", { items: this.items, currentPath: this.currentPath, searchQuery: this.searchQuery, loading: this.loading, searchArea: this.searchArea, searchEnv: this.searchEnv }));
534
+ }
535
+ return (index.h("nav", { "aria-label": "Zanichelli.it" }, index.h("div", { class: "shadow-wrapper" }, index.h("div", { class: "width-limiter" }, index.h("ul", { class: "menubar", role: "menubar", "aria-label": "Zanichelli.it" }, this.loading &&
536
+ [...new Array(4)].map((_, index$1) => (index.h(index.Fragment, null, index.h("li", { role: "none" }, index.h("div", { class: "menubar-item" }, index.h("z-ghost-loading", null))), index$1 < 3 && index.h("li", { role: "separator" })))), this.items?.map((item, index$1) => (index.h(index.Fragment, null, index.h("li", { role: "none" }, index.h("a", { class: {
537
+ 'menubar-item': true,
538
+ 'active': this.isActive(item),
539
+ }, href: item.href, id: item.id, role: "menuitem", tabIndex: -1, "aria-expanded": item.menuItems?.length ? (this.openMenu === item.id ? 'true' : 'false') : undefined, "aria-haspopup": item.menuItems?.length ? 'true' : 'false', "aria-current": this.current.includes(item.id) ? 'page' : 'false', onPointerOver: () => this.showMenu(item), onKeyDown: (event) => this.handleItemKeydown(event, item), target: item.target }, index.h("span", { "data-text": item.label }, item.label), item.menuItems?.length > 0 && (index.h("z-icon", { name: this.openMenu === item.id ? 'chevron-up' : 'chevron-down', width: "0.875rem", height: "0.875rem" })))), index$1 < this.items?.length - 1 && index.h("li", { role: "separator" }))))), index.h("zanit-search-form", { searchQuery: this.searchQuery, searchArea: this.searchArea, searchEnv: this.searchEnv, onResetSearch: () => (this.searchQuery = undefined) })), this.items.map((item) => this.openMenu === item.id && (index.h(Menu, { controlledBy: item.id, items: item.menuItems, currentPath: this.currentPath, onItemKeyDown: (event) => this.handleMenuKeydown(event) })))), this.items
540
+ ?.filter((item) => this.isActive(item))
541
+ .map((item) => item.navbarItems?.length && (index.h("nav", { class: { 'sub-menubar': true, 'shadow-wrapper': true }, "aria-label": `Sezioni: ${item.label}` }, index.h("ul", { role: "menubar" }, item.navbarItems.map((subitem) => (index.h(index.Fragment, null, index.h("li", { role: "none" }, index.h("a", { class: {
542
+ 'menubar-item': true,
543
+ 'active': this.isActive(subitem),
544
+ }, href: subitem.href, id: subitem.id, role: "menuitem", tabIndex: -1, "aria-haspopup": subitem.menuItems?.length ? 'true' : 'false', "aria-expanded": subitem.menuItems?.length ? (this.openMenu === subitem.id ? 'true' : 'false') : undefined, "aria-current": this.current.includes(subitem.id) ? 'page' : 'false', onPointerOver: () => this.showMenu(subitem), onKeyDown: (event) => this.handleItemKeydown(event, subitem), target: item.target }, index.h("span", null, subitem.label), subitem.menuItems?.length > 0 && (index.h("z-icon", { name: this.openMenu === subitem.id ? 'chevron-up' : 'chevron-down', width: "0.75rem", height: "0.75rem" })))))))), item.navbarItems.map((subitem) => this.openMenu === subitem.id && (index.h(Menu, { controlledBy: subitem.id, items: subitem.menuItems, currentPath: this.currentPath, onItemKeyDown: (event) => this.handleMenuKeydown(event) }))))))));
545
+ }
546
+ static get delegatesFocus() { return true; }
547
+ static get watchers() { return {
548
+ "data": ["parseData"],
549
+ "items": ["onItemsChange"],
550
+ "current": ["onCurrentChange"]
551
+ }; }
552
+ };
553
+ ZanitMenubar.style = menubarCss + menuCss$1;
554
+
555
+ const mobileMenubarCss = ":host{position:relative;z-index:2;display:block;width:100%;max-width:100%;height:3rem;background-color:#fff;color:var(--gray900);fill:var(--gray900);font-family:var(--font-family-sans)}:host,*,::before,::after{box-sizing:border-box}*:focus:focus-visible{box-shadow:var(--shadow-focus-primary);outline:none}ul{padding:0;margin:0;list-style:none}a{color:var(--gray900);cursor:pointer;text-decoration:none}button{all:unset;cursor:pointer}nav{display:flex;width:100%;align-items:center;padding-left:var(--grid-margin);gap:8px}nav::after{position:absolute;top:0;right:0;width:100%;height:100%;background:transparent;box-shadow:var(--shadow-1);content:'';pointer-events:none}z-logo{margin:8px 0}.mobile-menu{position:absolute;top:100%;left:0;display:flex;overflow:auto;width:100%;max-height:calc(100vh - 48px - var(--zanit-menubar-top-offset, 0px));flex-direction:column;padding:16px var(--grid-margin) 32px;background-color:#fff;box-shadow:var(--shadow-2);gap:8px}.mobile-menu li{width:100%}.mobile-menu .items-container{display:flex;min-height:256px;flex-direction:column;gap:8px}.mobile-menu .items-container z-ghost-loading{width:40%;height:1.2rem}.mobile-menu .items-container .menubar-item{display:block;width:100%;padding:8px 0;font-size:1rem;text-align:left}.mobile-menu .items-container li:not(:last-child) .menubar-item{border-bottom:1px solid #000}[role='menuitem'].parent{display:flex;width:fit-content;align-items:center;padding:0;border:none;font-size:0.875rem;gap:8px}zanit-search-form{margin-left:auto}";
556
+
557
+ const menuCss = ".menu-wrapper{width:100%;background-color:#fff}.menu{position:relative;display:flex;width:100%;flex-direction:column;gap:32px 0}.menu .group{display:flex;flex-direction:column}.menu .group .group-name{border-bottom:1px solid currentcolor;margin-bottom:4px;color:var(--red500);font-size:0.875rem;font-weight:var(--font-md)}.menu .group .menu-list{display:flex;flex-direction:column;gap:8px}.menu .group .menu-list .menu-item{display:block;border-bottom:2px solid transparent;font-size:0.875rem;font-weight:var(--font-md)}.menu .menu-list .menu-item.active,.menu .menu-list .menu-item:hover{border-bottom-color:var(--red500)}.menu .group.highlight .menu-list .menu-item{font-size:1rem}@media (width >= 768px){.menu-wrapper{position:absolute;top:100%;left:0;display:flex;justify-content:center;box-shadow:var(--shadow-1)}.menu{display:grid;width:100%;max-width:var(--zanit-menubar-max-width, 1366px);padding:16px var(--grid-margin);gap:0 24px;grid-auto-columns:minmax(0, max-content);grid-auto-flow:column;grid-template-rows:minmax(0, max-content) max-content}.menu .group{display:grid;grid-row:1 / -1;grid-template-columns:1fr;grid-template-rows:subgrid}@supports not (grid-template-rows: subgrid){.menu .group{grid-template-rows:repeat(auto-fit, minmax(0, max-content))}}.menu .group .group-name{border:none;margin-bottom:16px}.menu .group .menu-list .menu-item{font-size:1rem}.menu .group.highlight .menu-list .menu-item{font-size:1.5rem}}";
558
+
559
+ const ZanitMobileMenubar = class {
560
+ constructor(hostRef) {
561
+ index.registerInstance(this, hostRef);
562
+ }
563
+ get host() { return index.getElement(this); }
564
+ /** IDs path of the current item. */
565
+ currentPath = [];
566
+ /** Menubar items. */
567
+ items = [];
568
+ /** Initial search query. */
569
+ searchQuery = undefined;
570
+ /** Whether the menubar is loading the data. */
571
+ loading = false;
572
+ /** Environment for search suggestions */
573
+ searchEnv = SearchEnv.PROD;
574
+ /** Search area (e.g. "SCUOLA", "UNIVERSITÀ", "DIZIONARI"). */
575
+ searchArea;
576
+ /** Last active item ID. */
577
+ lastCurrent = undefined;
578
+ parentItem = undefined;
579
+ menuItems = undefined;
580
+ /** Whether the items to render come from a menubar or a menu. */
581
+ menuType = undefined;
582
+ open;
583
+ onItemsChange() {
584
+ this.lastCurrent = this.currentPath?.length ? this.currentPath[this.currentPath.length - 1] : undefined;
585
+ this.setupData(this.items);
586
+ }
587
+ /**
588
+ * Find the current item and take its parent, `menuItems` or the `navbarItems`.
589
+ */
590
+ setupData(items, parent) {
591
+ // If no current item is defined, we show all items
592
+ if (this.lastCurrent === undefined) {
593
+ this.parentItem = undefined;
594
+ this.menuType = 'menubar';
595
+ this.menuItems = items;
596
+ return;
597
+ }
598
+ for (const item of items) {
599
+ if (item.id === this.lastCurrent) {
600
+ this.parentItem = parent;
601
+ this.menuType = item.menuItems?.length ? 'menu' : 'menubar';
602
+ this.menuItems = item.menuItems || item.navbarItems;
603
+ return;
604
+ }
605
+ if (this.currentPath.length > 1 &&
606
+ item.id === this.currentPath[this.currentPath.length - 2] &&
607
+ item.menuItems?.some(({ id }) => id === this.lastCurrent)) {
608
+ this.parentItem = item;
609
+ this.menuType = item.menuItems?.length ? 'menu' : 'menubar';
610
+ this.menuItems = item.menuItems || item.navbarItems;
611
+ return;
612
+ }
613
+ if (item.navbarItems?.length) {
614
+ this.setupData(item.navbarItems, item);
615
+ }
616
+ }
617
+ }
618
+ get menuItemsElement() {
619
+ return Array.from(this.host.shadowRoot.querySelectorAll('[role="menuitem"]'));
620
+ }
621
+ /** Initialize tabindex on menuitems, setting -1 to all but the first one. */
622
+ initTabindex() {
623
+ this.menuItemsElement.forEach((item, index) => item.setAttribute('tabindex', index === 0 ? '0' : '-1'));
624
+ }
625
+ toggleMenu() {
626
+ if (this.open) {
627
+ this.open = false;
628
+ }
629
+ else {
630
+ this.open = true;
631
+ setTimeout(() => {
632
+ this.initTabindex();
633
+ this.menuItemsElement[0]?.focus({ preventScroll: true });
634
+ }, 200);
635
+ }
636
+ }
637
+ /** Handles keyboard navigation on mobile menu. */
638
+ handleItemKeydown(event) {
639
+ switch (event.key) {
640
+ case 'ArrowUp': {
641
+ event.preventDefault();
642
+ event.stopPropagation();
643
+ const items = this.menuItemsElement;
644
+ const currentIndex = items.indexOf(event.target);
645
+ const prevItem = items[(currentIndex - 1 + items.length) % items.length];
646
+ moveFocus(items[currentIndex], prevItem);
647
+ break;
648
+ }
649
+ case 'ArrowDown': {
650
+ event.preventDefault();
651
+ event.stopPropagation();
652
+ const items = this.menuItemsElement;
653
+ const currentIndex = items.indexOf(event.target);
654
+ const nextItem = items[(currentIndex + 1) % items.length];
655
+ moveFocus(items[currentIndex], nextItem);
656
+ break;
657
+ }
658
+ case 'Home': {
659
+ event.preventDefault();
660
+ event.stopPropagation();
661
+ moveFocus(event.target, this.menuItemsElement[0]);
662
+ break;
663
+ }
664
+ case 'End': {
665
+ event.preventDefault();
666
+ event.stopPropagation();
667
+ moveFocus(event.target, this.menuItemsElement.pop());
668
+ break;
669
+ }
670
+ }
671
+ }
672
+ connectedCallback() {
673
+ this.lastCurrent = this.currentPath?.length ? this.currentPath[this.currentPath.length - 1] : undefined;
674
+ this.setupData(this.items);
675
+ }
676
+ /** Close the menu when clicking outside. */
677
+ handleOutsideClick(event) {
678
+ if (containsTarget(this.host, event)) {
679
+ return;
680
+ }
681
+ this.open = false;
682
+ }
683
+ /** Close the menu when pressing Escape or Tab. */
684
+ handleKeydown(event) {
685
+ switch (event.key) {
686
+ case 'Escape':
687
+ this.open = false;
688
+ break;
689
+ case 'Tab':
690
+ if (containsTarget(this.host, event)) {
691
+ break;
692
+ }
693
+ this.open = false;
694
+ break;
695
+ }
696
+ }
697
+ /** Close the menu when the focus goes out. */
698
+ handleFocusout(event) {
699
+ if (containsTarget(this.host, event)) {
700
+ return;
701
+ }
702
+ this.open = false;
703
+ }
704
+ render() {
705
+ return (index.h("nav", { key: '76079ce1e66f1b568f6e7eac1f67b91af0f2f48a', "aria-label": "Zanichelli.it" }, index.h("button", { key: '630b16be60aba3d7027fbe4b9e19e440cb454267', class: "burger-button", type: "button", "aria-expanded": this.open ? 'true' : 'false', "aria-controls": "mobile-menu", "aria-label": this.open ? 'Chiudi menù' : 'Apri menù', onClick: () => this.toggleMenu() }, index.h("z-icon", { key: '4c86d84d421ab93fb1c815c5a667dd55ce8254ac', name: this.open ? 'multiply' : 'burger-menu', width: "1.5rem", height: "1.5rem" })), index.h("z-logo", { key: '03c7b8613698098cf3a1ff4b1b8d649cb7d5b92a', imageAlt: "Logo Zanichelli", link: "/", height: 32, width: 126 }), index.h("zanit-search-form", { key: '5a1385991eebcf17ac8c275c8c69f6662f75e3b7', searchQuery: this.searchQuery, onResetSearch: () => (this.searchQuery = undefined), searchArea: this.searchArea, searchEnv: this.searchEnv }), this.open && (index.h("ul", { key: 'bc627e55f902beaac658f02d90ed78836fde193b', class: "mobile-menu", role: "menubar" }, !this.loading && this.currentPath && this.currentPath.length > 0 && (index.h("li", { key: '4708f9ee160d62a6cbf6333e11c1b9d02ae3d34c', role: "none" }, index.h("a", { key: '9fd59125f8cb87f6c86108eea7ba79a04bc4d68c', class: "parent", href: this.parentItem?.href ?? '/', id: this.parentItem?.id ?? undefined, role: "menuitem", tabIndex: -1, onKeyDown: (event) => this.handleItemKeydown(event), target: this.parentItem?.target }, index.h("z-icon", { key: '615bc59a1f058a745bdadd90c6f51f9abc757df1', name: "arrow-left", width: "0.5rem", height: "0.5rem" }), index.h("span", { key: '66a80691639e97b2d7d7d61cc45f8b47dcdabafe' }, this.parentItem?.label || 'Home')))), this.loading ? (index.h("div", { class: "items-container", role: "none" }, [...new Array(4)].map(() => (index.h("li", { role: "none" }, index.h("div", { class: "menubar-item", role: "none" }, index.h("z-ghost-loading", null))))))) : this.menuType === 'menu' ? (index.h(Menu, { items: this.menuItems, controlledBy: this.parentItem?.id, currentPath: this.currentPath, onItemKeyDown: (event) => this.handleItemKeydown(event) })) : (this.menuItems?.length > 0 && (index.h("div", { class: "items-container", role: "none" }, this.menuItems.map((item) => (index.h("li", { role: "none" }, index.h("a", { class: {
706
+ 'menu-item': this.menuType === 'menu',
707
+ 'menubar-item': this.menuType === 'menubar',
708
+ }, href: item.href, id: item.id, role: "menuitem", "aria-current": this.lastCurrent === item.id ? 'page' : 'false', tabIndex: -1, onKeyDown: (event) => this.handleItemKeydown(event), target: item.target }, index.h("span", { "data-text": item.label }, item.label))))))))))));
709
+ }
710
+ static get delegatesFocus() { return true; }
711
+ static get watchers() { return {
712
+ "items": ["onItemsChange"],
713
+ "currentPath": ["onItemsChange"]
714
+ }; }
715
+ };
716
+ ZanitMobileMenubar.style = mobileMenubarCss + menuCss;
717
+
718
+ var AREA_LABELS;
719
+ (function (AREA_LABELS) {
720
+ AREA_LABELS["SCUOLA"] = "Scuola";
721
+ AREA_LABELS["UNIVERSIT\u00C0"] = "Universit\u00E0";
722
+ AREA_LABELS["GIURIDICO"] = "Giuridico";
723
+ AREA_LABELS["DIZIONARI"] = "Dizionari";
724
+ AREA_LABELS["SAGGISTICA"] = "Saggistica";
725
+ })(AREA_LABELS || (AREA_LABELS = {}));
726
+ const AREA_ORDER = Object.keys(AREA_LABELS);
727
+ function buildSuggestions(query, subjectsByArea, selectedArea) {
728
+ const matchingSubjectAreas = findSubjectAreas(query, subjectsByArea);
729
+ const hasSubject = matchingSubjectAreas.length > 0;
730
+ const subject = hasSubject ? query.toUpperCase() : undefined;
731
+ const suggestions = [];
732
+ if (selectedArea)
733
+ suggestions.push(buildWordSuggestion(query, selectedArea));
734
+ suggestions.push(buildWordSuggestion(query));
735
+ if (hasSubject) {
736
+ if (selectedArea) {
737
+ const orderedSubjectAreas = [
738
+ ...matchingSubjectAreas.filter((area) => area === selectedArea),
739
+ ...matchingSubjectAreas
740
+ .filter((area) => area !== selectedArea)
741
+ .sort((a, b) => getAreaOrder(a) - getAreaOrder(b)),
742
+ ];
743
+ orderedSubjectAreas.forEach((area) => suggestions.push(buildSubjectSuggestion(query, area, subject)));
744
+ }
745
+ else {
746
+ matchingSubjectAreas
747
+ .sort((a, b) => getAreaOrder(a) - getAreaOrder(b))
748
+ .forEach((subjectArea) => suggestions.push(buildSubjectSuggestion(query, subjectArea, subject)));
749
+ }
750
+ }
751
+ return suggestions;
752
+ }
753
+ const buildWordSuggestion = (user_query, area) => {
754
+ return {
755
+ id: buildId(`word-${user_query}-${area}`),
756
+ label: buildLabel(user_query, area),
757
+ aria_label: buildAriaLabel(user_query, area, false),
758
+ url: buildUrl({ q: user_query, ...(area ? { area } : {}), user_query }),
759
+ ...buildDetail(user_query, user_query, area),
760
+ };
761
+ };
762
+ const buildSubjectSuggestion = (user_query, area, subject) => {
763
+ return {
764
+ id: buildId(`subj-${user_query}-${area}-${subject}`),
765
+ label: buildLabel(user_query, area),
766
+ aria_label: buildAriaLabel(user_query, area, true),
767
+ url: buildUrl({ area, materia: subject, user_query }),
768
+ ...buildDetail(user_query, undefined, area, subject),
769
+ };
770
+ };
771
+ const buildId = (string) => string
772
+ .split('')
773
+ .map((c) => c.charCodeAt(0).toString(16))
774
+ .join('');
775
+ const buildUrl = (params) => {
776
+ return `ricerca?${new URLSearchParams(params).toString()}`;
777
+ };
778
+ const buildDetail = (user_query, query, area, subject) => ({
779
+ user_query,
780
+ ...(query ? { query } : {}),
781
+ ...(area ? { area } : {}),
782
+ ...(subject ? { subject } : {}),
783
+ });
784
+ const buildLabel = (user_query, area) => {
785
+ return `<mark>${user_query}</mark> in <strong>${area ? `${AREA_LABELS[area] ?? area}` : `tutto il sito`}</strong>`;
786
+ };
787
+ const buildAriaLabel = (user_query, area, isSubject = false) => {
788
+ return `Cerca la ${isSubject ? `materia` : `parola`} ${user_query} ${area ? `nel catalogo ${AREA_LABELS[area] ?? area}` : `in tutto il sito`}`;
789
+ };
790
+ function findSubjectAreas(query, subjectsByArea) {
791
+ const cleanedQuery = cleanSearch(query);
792
+ return Object.entries(subjectsByArea)
793
+ .filter(([, subjects]) => subjects.some((subject) => subject.toLowerCase() === cleanedQuery))
794
+ .map(([area]) => area);
795
+ }
796
+ /** Clear search string: lowercase, remove multiple spaces */
797
+ const cleanSearch = (s) => s.toLowerCase().replace(/\s+/g, ' ');
798
+ const getAreaOrder = (area) => {
799
+ const index = AREA_ORDER.indexOf(area);
800
+ return index >= 0 ? index : 100;
801
+ };
802
+
803
+ const searchFormCss = ":host,*,::before,::after{box-sizing:border-box}*:focus:focus-visible{box-shadow:var(--shadow-focus-primary);outline:none}button{all:unset;cursor:pointer}.searchbar{--searchbar-button-x-padding:14px;--searchbar-button-icon-width:1.75rem;--closed-searchbar-width:calc((var(--searchbar-button-x-padding) * 2) + var(--searchbar-button-icon-width) + 1px);position:absolute;z-index:5;top:0;right:0;display:flex;width:var(--closed-searchbar-width);height:3rem;justify-content:flex-end;transition:width 0.4s ease-in-out}.searchbar.searchbar-open{width:100%}.searchbar .input-wrapper{display:flex;overflow:hidden;width:100%;align-items:center;padding:8px;padding-left:var(--grid-margin);background-color:#fff;gap:8px;transition-duration:0.4s;transition-property:padding-right, padding-left, width;transition-timing-function:ease-in-out}.searchbar:not(.searchbar-open) .input-wrapper{overflow:hidden;width:0;padding:0}.searchbar button[type='reset']{--z-icon-width:1rem;--z-icon-height:1rem;display:flex;align-items:center;cursor:pointer}.searchbar input{z-index:1;width:100%;height:100%;padding:0;border:none;background-color:#fff;font-family:var(--font-family-sans);font-size:1rem}.searchbar.searchbar-open input:first-child{padding-left:4px;margin-left:-4px;}.searchbar input[type='search']::-webkit-search-cancel-button,.searchbar input[type='search']::-webkit-search-decoration{appearance:none}.searchbar input::placeholder{color:var(--gray500)}.searchbar .searchbar-button{display:flex;align-items:center;justify-content:center;padding:10px var(--searchbar-button-x-padding);border-left:1px solid #000;background:var(--zanit-accent-color);font-family:inherit;font-size:inherit;gap:64px;line-height:1}.searchbar .searchbar-button:focus-visible{z-index:1}.searchbar-button z-icon{--z-icon-width:var(--searchbar-button-icon-width);--z-icon-height:var(--searchbar-button-icon-width)}.suggestions-wrapper{position:absolute;z-index:4;top:3rem;left:50%;width:100vw;border-top:1px solid var(--gray200);margin-left:-50vw;background:#fff;box-shadow:var(--shadow-1)}.suggestions-wrapper.hidden{display:none}.suggestions{display:flex;width:100%;flex-direction:column;align-items:stretch;padding:var(--space-unit);margin:0 auto}.suggestion-head{padding:calc(var(--space-unit) * 0.75) var(--space-unit);color:var(--gray700);font-size:0.875rem;font-weight:var(--font-md);line-height:1.125rem}.suggestion{display:flex;padding:calc(var(--space-unit) * 0.75) var(--space-unit);color:var(--gray900);cursor:pointer;font-size:1rem;gap:var(--space-unit);line-height:1.5rem}.suggestion:hover,.suggestion[aria-selected='true']{background:var(--gray100)}.suggestion strong{font-weight:var(--font-bd)}.suggestion mark{background-color:var(--red50)}.suggestion z-icon{--z-icon-height:1.125rem;margin-top:0.125rem}.suggestions z-divider{margin:var(--space-unit) 0}@media (width < 1152px){.searchbar .searchbar-button>.searchbar-button-label{display:none}}@media (width >= 768px){.searchbar{--searchbar-button-x-padding:16px;--searchbar-button-icon-width:2rem}.searchbar .input-wrapper{gap:14px}.searchbar button[type='reset']{--z-icon-width:1.5rem;--z-icon-height:1.5rem}.searchbar input,.searchbar .searchbar-button{font-size:1.5rem}.searchbar .searchbar-button{padding:8px var(--searchbar-button-x-padding)}.suggestions{padding:var(--space-unit) calc(var(--space-unit) * 2)}}@media (width >= 1152px){.searchbar{--closed-searchbar-width:190px}}@media (width >= 1366px){.searchbar .searchbar-button{border-right:1px solid #000}.suggestions{max-width:1366px;padding:var(--space-unit) calc(var(--space-unit) * 3)}}";
804
+
805
+ const ZanitSearchForm = class {
806
+ constructor(hostRef) {
807
+ index.registerInstance(this, hostRef);
808
+ this.search = index.createEvent(this, "search");
809
+ this.resetSearch = index.createEvent(this, "resetSearch");
810
+ }
811
+ formElement;
812
+ subjectsByArea = {};
813
+ timer;
814
+ get host() { return index.getElement(this); }
815
+ /** Indicates whether the searchbar is visible and usable. */
816
+ showSearchbar = false;
817
+ /** Search query to apply. */
818
+ _searchQuery = undefined;
819
+ /** Search suggestions to show in the autocomplete dropdown. */
820
+ suggestions = [];
821
+ /** Active suggestion - used for keyboard navigation */
822
+ activeSuggestion = '';
823
+ /** Show suggestions list */
824
+ showSuggestions = false;
825
+ /** Initial search query */
826
+ searchQuery = undefined;
827
+ /** Environment for search suggestions */
828
+ searchEnv = SearchEnv.PROD;
829
+ /** Search area (e.g. "SCUOLA", "UNIVERSITÀ", "DIZIONARI"). */
830
+ searchArea;
831
+ onSearchQueryChange() {
832
+ this._searchQuery = this.searchQuery;
833
+ if (this.searchQuery) {
834
+ this.openSearchbar();
835
+ }
836
+ this.resetSuggestions();
837
+ }
838
+ onSearchAreaChange() {
839
+ this.resetSuggestions();
840
+ }
841
+ onShowSearchbarChange() {
842
+ if (!this.showSearchbar) {
843
+ this.showSuggestions = false;
844
+ }
845
+ }
846
+ onShowSuggestionsChange() {
847
+ this.activeSuggestion = '';
848
+ }
849
+ /** Emitted on search form submission. */
850
+ search;
851
+ resetSearch;
852
+ async connectedCallback() {
853
+ this.subjectsByArea = await getSubjectsByArea(this.searchEnv);
854
+ this.showSearchbar = !!this.searchQuery;
855
+ this._searchQuery = this.searchQuery;
856
+ }
857
+ /** Close open searchbar when clicking outside. */
858
+ handleOutsideClick(event) {
859
+ if (this.showSearchbar && this.host && !containsTarget(this.host, event)) {
860
+ this.showSearchbar = false;
861
+ }
862
+ }
863
+ /** Close the searchbar/suggestions when pressing Escape. */
864
+ handleEsc(event) {
865
+ if (!isEscKey(event)) {
866
+ return;
867
+ }
868
+ if (this.showSuggestions) {
869
+ this.showSuggestions = false;
870
+ }
871
+ else {
872
+ this.showSearchbar = false;
873
+ }
874
+ }
875
+ /** Close the searchbar/suggestions when pressing Tab. */
876
+ handleTab(event) {
877
+ if (!isTabKey(event)) {
878
+ return;
879
+ }
880
+ this.showSuggestions = false;
881
+ if (!containsTarget(this.host, event)) {
882
+ this.showSearchbar = false;
883
+ }
884
+ }
885
+ openSearchbar() {
886
+ this.showSearchbar = true;
887
+ setTimeout(() => {
888
+ const searchbarInput = this.host.shadowRoot.querySelector('#searchbar-input');
889
+ searchbarInput.focus({ preventScroll: true });
890
+ }, 500);
891
+ }
892
+ resetSearchQuery() {
893
+ this.searchQuery = undefined;
894
+ this.resetSearch.emit();
895
+ }
896
+ resetSuggestions() {
897
+ this.suggestions = [];
898
+ this.showSuggestions = false;
899
+ }
900
+ handleInputChange(event) {
901
+ this._searchQuery = event.target.value;
902
+ if (!this._searchQuery) {
903
+ this.searchQuery = undefined;
904
+ }
905
+ this.updateSuggestions();
906
+ }
907
+ updateSuggestions() {
908
+ clearTimeout(this.timer);
909
+ const query = (this._searchQuery || '').trim();
910
+ if (query.length < 3) {
911
+ this.resetSuggestions();
912
+ return;
913
+ }
914
+ this.timer = setTimeout(() => {
915
+ this.resetSuggestions();
916
+ this.suggestions = buildSuggestions(query, this.subjectsByArea, this.searchArea?.toUpperCase());
917
+ this.showSuggestions = true;
918
+ }, 300);
919
+ }
920
+ onSearchSubmit(event) {
921
+ event.preventDefault();
922
+ if (!this._searchQuery) {
923
+ return;
924
+ }
925
+ if (this.activeSuggestion) {
926
+ const suggestion = this.suggestions.find((s) => s.id === this.activeSuggestion);
927
+ if (suggestion) {
928
+ this.submitSuggestionSearch(suggestion);
929
+ this.showSuggestions = false;
930
+ return;
931
+ }
932
+ }
933
+ this.showSearchbar = false;
934
+ const searchEv = this.search.emit({ query: this._searchQuery, area: this.searchArea });
935
+ // do not submit the form if the event default behavior was prevented
936
+ if (searchEv.defaultPrevented) {
937
+ return;
938
+ }
939
+ this.formElement.submit();
940
+ }
941
+ submitSuggestionSearch(suggestion) {
942
+ const ev = this.search.emit({
943
+ user_query: suggestion.user_query,
944
+ query: suggestion.query,
945
+ area: suggestion.area,
946
+ subject: suggestion.subject,
947
+ });
948
+ if (!ev.defaultPrevented) {
949
+ window.location.href = suggestion.url;
950
+ }
951
+ }
952
+ handleSuggestionsNav(event) {
953
+ if (!isArrowDownKey(event) && !isArrowUpKey(event)) {
954
+ return;
955
+ }
956
+ if (!this.suggestions.length) {
957
+ return;
958
+ }
959
+ const options = this.suggestions.map((o) => o.id);
960
+ if (!options.length) {
961
+ return;
962
+ }
963
+ event.preventDefault();
964
+ event.stopPropagation();
965
+ if (!this.showSuggestions) {
966
+ this.showSuggestions = true;
967
+ }
968
+ let nextId = null;
969
+ const firstId = options[0];
970
+ const lastId = options[options.length - 1];
971
+ const currOption = options.indexOf(this.activeSuggestion);
972
+ if (currOption < 0) {
973
+ nextId = isArrowDownKey(event) ? firstId : lastId;
974
+ }
975
+ else {
976
+ if (isArrowDownKey(event)) {
977
+ nextId = options[currOption + 1] || lastId;
978
+ }
979
+ else {
980
+ nextId = options[currOption - 1] || firstId;
981
+ }
982
+ }
983
+ this.activeSuggestion = nextId;
984
+ }
985
+ renderSuggestions() {
986
+ const renderHeading = (label, key) => (index.h("span", { key: key, class: "suggestion-head", "aria-hidden": "true" }, label));
987
+ return (index.h("div", { class: { 'suggestions-wrapper': true, 'hidden': !this.showSuggestions || !this.suggestions.length }, onPointerOver: (e) => e.preventDefault() }, index.h("div", { id: "search-suggestions", class: "suggestions", role: "listbox", "aria-label": "Seleziona tra i suggerimenti" }, this.suggestions.map((suggestion, k) => {
988
+ const items = [];
989
+ if (k === 0) {
990
+ items.push(renderHeading('Cerca la parola', 'word-head'));
991
+ }
992
+ else if (suggestion.subject && !this.suggestions[k - 1].subject) {
993
+ items.push(index.h("z-divider", { "aria-hidden": "true" }));
994
+ items.push(renderHeading('Cerca la materia', 'subj-head'));
995
+ }
996
+ items.push(index.h("span", { key: k, id: suggestion.id, class: "suggestion", role: "option", "aria-label": suggestion.aria_label, "aria-selected": this.activeSuggestion === suggestion.id ? 'true' : undefined, onClick: () => this.submitSuggestionSearch(suggestion) }, index.h("z-icon", { name: "left-magnifying-glass" }), index.h("span", { "aria-hidden": "true", innerHTML: suggestion.label })));
997
+ return items;
998
+ }))));
999
+ }
1000
+ render() {
1001
+ return (index.h(index.Host, { key: '9535c68b8cd3e76a55785cb993bed5026f84a589' }, index.h("form", { key: '26271b8f9034b3ea64bbea6ff413e75d0e857612', class: { 'searchbar': true, 'searchbar-open': this.showSearchbar }, ref: (el) => (this.formElement = el), role: "search", "aria-label": "Cerca nel sito", method: "get", action: "/ricerca", onSubmit: (event) => this.onSearchSubmit(event), onReset: () => this.resetSearchQuery() }, !!this.searchArea && (index.h("input", { key: '90e9dc4252a02a00db5160d374bd4ea42c4dce17', type: "hidden", name: "area", value: this.searchArea })), index.h("div", { key: '6189f91f8cbf9086c1dcddac5a1b649065d4319c', class: "input-wrapper", role: "none" }, this.searchQuery && (index.h("button", { key: '6292dc5e90576b49993ed15d908c675b501568d9', type: "reset", "aria-label": "Svuota campo di ricerca", disabled: !this.showSearchbar, "aria-hidden": !this.showSearchbar ? 'true' : undefined, tabIndex: !this.showSearchbar ? -1 : 0 }, index.h("z-icon", { key: '67f07f13b459afad718d2687f5feea4271d6ec6a', name: "multiply-circled" }))), index.h("input", { key: 'ee4f88993f3bf1ba768eaf1b6ff63cd346bd552e', id: "searchbar-input", name: "q", type: "search", disabled: !this.showSearchbar, placeholder: "Cerca per parola chiave o ISBN", value: this.searchQuery, required: true, autocomplete: "off", role: "combobox", "aria-autocomplete": "list", "aria-expanded": this.showSuggestions ? 'true' : 'false', "aria-controls": "search-suggestions", "aria-activedescendant": this.activeSuggestion, "aria-label": "Cerca per parola chiave o ISBN", "aria-hidden": !this.showSearchbar ? 'true' : undefined, tabIndex: !this.showSearchbar ? -1 : 0, onInput: (event) => this.handleInputChange(event), onKeyDown: (e) => {
1002
+ // INFO: prevent ESC from clearing input
1003
+ if (isEscKey(e)) {
1004
+ e.preventDefault();
1005
+ }
1006
+ this.handleSuggestionsNav(e);
1007
+ } })), index.h("button", { key: '4e0feaeaff7bcecaf6a29409a6accac895810fdf', class: "searchbar-button", "aria-label": this.showSearchbar ? 'Esegui ricerca' : 'Apri il campo di ricerca', "aria-controls": "searchbar-input", type: this.showSearchbar ? 'submit' : 'button', onClick: () => this.openSearchbar() }, this.showSearchbar ? null : index.h("span", { class: "searchbar-button-label" }, "Cerca"), index.h("z-icon", { key: '49eec7e6f04c34df57fcf99f405d1c3ada1fcdfb', name: "search" }))), this.renderSuggestions()));
1008
+ }
1009
+ static get watchers() { return {
1010
+ "searchQuery": ["onSearchQueryChange"],
1011
+ "searchArea": ["onSearchAreaChange"],
1012
+ "showSearchbar": ["onShowSearchbarChange"],
1013
+ "showSuggestions": ["onShowSuggestionsChange"]
1014
+ }; }
1015
+ };
1016
+ ZanitSearchForm.style = searchFormCss;
1017
+
1018
+ exports.zanit_back_to_top = ZanitBackTop;
1019
+ exports.zanit_menubar = ZanitMenubar;
1020
+ exports.zanit_mobile_menubar = ZanitMobileMenubar;
1021
+ exports.zanit_search_form = ZanitSearchForm;
1022
+ //# sourceMappingURL=zanit-back-to-top.zanit-menubar.zanit-mobile-menubar.zanit-search-form.entry.cjs.js.map
1023
+
1024
+ //# sourceMappingURL=zanit-back-to-top_4.cjs.entry.js.map