@iris-ui-kit/vue 0.2.26 → 0.2.28

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.
package/dist/index.cjs CHANGED
@@ -917,6 +917,7 @@ var CSS2 = `
917
917
  --iris-nav-item-padding-block: 8px;
918
918
  --iris-nav-item-border-radius: var(--iris-radius-md, 6px);
919
919
  --iris-nav-item-hover: var(--iris-surface-hover, var(--iris-surface));
920
+ --iris-nav-item-active-hover: color-mix(in srgb, var(--iris-primary) 88%, black);
920
921
  --iris-nav-item-height: 34px;
921
922
  display: flex;
922
923
  flex-direction: column;
@@ -1022,6 +1023,13 @@ var CSS2 = `
1022
1023
  font-weight: 600;
1023
1024
  }
1024
1025
 
1026
+ /* active \u9879\u7684 hover \u53CD\u9988\uFF1Aprimary \u52A0\u6DF1\u53D8\u4F53\uFF08\u76AE\u80A4\u53EF\u7528
1027
+ --iris-nav-item-active-hover \u8986\u76D6\uFF09\uFF1B\u7279\u5F02\u5EA6\u9AD8\u4E8E :hover/:where \u89C4\u5219 */
1028
+ .iris-nav-menu-item[data-active='true']:hover,
1029
+ .iris-nav-menu-item[data-active='true'].is-hovered:not([disabled], .is-disabled) {
1030
+ background: var(--iris-nav-item-active-hover, color-mix(in srgb, var(--iris-primary) 88%, black));
1031
+ }
1032
+
1025
1033
  :where(.iris-nav-menu-item[data-active-trail='true']) {
1026
1034
  color: var(--iris-primary);
1027
1035
  font-weight: 600;
@@ -1188,10 +1196,317 @@ function installNavMenuStyles() {
1188
1196
  document.head.appendChild(style);
1189
1197
  installed2 = true;
1190
1198
  }
1199
+ var HOVER_OPEN_DELAY_VAL = 60;
1200
+ var HOVER_CLOSE_DELAY_VAL = 150;
1201
+ function createNavMenuFlyout(ctx) {
1202
+ const hovered = vue.ref(null);
1203
+ const hoveredBranches = vue.ref([]);
1204
+ const focusedBranches = vue.ref([]);
1205
+ const clickedBranches = vue.ref([]);
1206
+ let suppressFocusOpen = false;
1207
+ let openTimer = null;
1208
+ const closeTimers = /* @__PURE__ */ new Map();
1209
+ const clearOpenTimer = () => {
1210
+ if (openTimer) {
1211
+ clearTimeout(openTimer);
1212
+ openTimer = null;
1213
+ }
1214
+ };
1215
+ const cancelClose = (key) => {
1216
+ const timer = closeTimers.get(key);
1217
+ if (timer) {
1218
+ clearTimeout(timer);
1219
+ closeTimers.delete(key);
1220
+ }
1221
+ };
1222
+ const cancelAllTimers = () => {
1223
+ clearOpenTimer();
1224
+ for (const timer of closeTimers.values()) clearTimeout(timer);
1225
+ closeTimers.clear();
1226
+ };
1227
+ vue.onBeforeUnmount(cancelAllTimers);
1228
+ const flyoutTriggers = /* @__PURE__ */ new Map();
1229
+ const flyoutPanels = /* @__PURE__ */ new Map();
1230
+ const setFlyoutRef = (map, key, el) => {
1231
+ if (el instanceof HTMLElement) map.set(key, el);
1232
+ else map.delete(key);
1233
+ };
1234
+ const positionFlyout = (key) => {
1235
+ const trigger = flyoutTriggers.get(key);
1236
+ const panel = flyoutPanels.get(key);
1237
+ if (!trigger || !panel || typeof document === "undefined") return;
1238
+ const rect = trigger.getBoundingClientRect();
1239
+ const dir = getComputedStyle(trigger).direction;
1240
+ panel.style.position = "fixed";
1241
+ panel.style.top = `${ctx.collapsed ? rect.top : rect.bottom}px`;
1242
+ if (dir === "rtl") {
1243
+ panel.style.left = "";
1244
+ panel.style.right = `${window.innerWidth - (ctx.collapsed ? rect.left : rect.right)}px`;
1245
+ } else {
1246
+ panel.style.right = "";
1247
+ panel.style.left = `${ctx.collapsed ? rect.right : rect.left}px`;
1248
+ }
1249
+ };
1250
+ vue.onBeforeUnmount(() => {
1251
+ detachViewportListeners();
1252
+ flyoutTriggers.clear();
1253
+ flyoutPanels.clear();
1254
+ });
1255
+ const isolateAtDepth = (key) => {
1256
+ const depth = core.findNavPath(ctx.items, key).length - 1;
1257
+ const isSameDepthOther = (k) => k !== key && core.findNavPath(ctx.items, k).length - 1 === depth;
1258
+ for (const state of [hoveredBranches, clickedBranches, focusedBranches]) {
1259
+ if (state.value.some(isSameDepthOther)) {
1260
+ state.value = state.value.filter((k) => !isSameDepthOther(k));
1261
+ }
1262
+ }
1263
+ };
1264
+ const scheduleOpen = (key) => {
1265
+ clearOpenTimer();
1266
+ openTimer = setTimeout(() => {
1267
+ openTimer = null;
1268
+ isolateAtDepth(key);
1269
+ setBranchInteraction(hoveredBranches, key, true);
1270
+ }, HOVER_OPEN_DELAY_VAL);
1271
+ };
1272
+ const scheduleClose = (key) => {
1273
+ cancelClose(key);
1274
+ closeTimers.set(
1275
+ key,
1276
+ setTimeout(() => {
1277
+ closeTimers.delete(key);
1278
+ setBranchInteraction(hoveredBranches, key, false);
1279
+ }, HOVER_CLOSE_DELAY_VAL)
1280
+ );
1281
+ };
1282
+ const closeHorizontalMenus = () => {
1283
+ if (!ctx.flyoutMode.value) return;
1284
+ cancelAllTimers();
1285
+ hovered.value = null;
1286
+ hoveredBranches.value = [];
1287
+ focusedBranches.value = [];
1288
+ clickedBranches.value = [];
1289
+ };
1290
+ const select = (node) => {
1291
+ if (node.disabled) return;
1292
+ closeHorizontalMenus();
1293
+ ctx.emitSelect(node.key, node);
1294
+ };
1295
+ const toggleFlyout = (key) => {
1296
+ cancelAllTimers();
1297
+ const current = clickedBranches.value;
1298
+ if (current.includes(key)) {
1299
+ clickedBranches.value = current.filter((k) => k !== key);
1300
+ setBranchInteraction(hoveredBranches, key, false);
1301
+ setBranchInteraction(focusedBranches, key, false);
1302
+ } else {
1303
+ isolateAtDepth(key);
1304
+ clickedBranches.value = [...clickedBranches.value, key];
1305
+ }
1306
+ };
1307
+ const openFlyout = (key) => {
1308
+ cancelAllTimers();
1309
+ isolateAtDepth(key);
1310
+ setBranchInteraction(focusedBranches, key, true);
1311
+ };
1312
+ const setBranchInteraction = (state, key, enabled) => {
1313
+ const current = state.value;
1314
+ if (enabled) {
1315
+ if (!current.includes(key)) state.value = [...current, key];
1316
+ } else if (current.includes(key)) {
1317
+ state.value = current.filter((item) => item !== key);
1318
+ }
1319
+ };
1320
+ const keepsPointerInside = (container, event) => {
1321
+ if (!container || !(container instanceof HTMLElement)) return false;
1322
+ const next = event.relatedTarget;
1323
+ return !!(next && next instanceof Node && container.contains(next));
1324
+ };
1325
+ const interactionKeyAtDepth = (keys, depth) => keys.find((key) => core.findNavPath(ctx.items, key).length === depth + 1);
1326
+ const horizontalBranchVisible = (key, depth) => {
1327
+ const hoveredAtDepth = interactionKeyAtDepth(hoveredBranches.value, depth);
1328
+ if (hoveredAtDepth) return hoveredAtDepth === key;
1329
+ const clickedAtDepth = interactionKeyAtDepth(clickedBranches.value, depth);
1330
+ if (clickedAtDepth) return clickedAtDepth === key;
1331
+ return interactionKeyAtDepth(focusedBranches.value, depth) === key;
1332
+ };
1333
+ const shownFlyoutKeys = vue.computed(() => {
1334
+ if (!ctx.flyoutMode.value) return [];
1335
+ const keys = [];
1336
+ for (const node of ctx.tree.value) {
1337
+ if (core.isBranch(node) && horizontalBranchVisible(node.key, 0)) keys.push(node.key);
1338
+ }
1339
+ return keys;
1340
+ });
1341
+ const repositionOpenFlyouts = () => {
1342
+ for (const key of shownFlyoutKeys.value) positionFlyout(key);
1343
+ };
1344
+ let viewportListenersActive = false;
1345
+ const attachViewportListeners = () => {
1346
+ if (viewportListenersActive || typeof document === "undefined") return;
1347
+ viewportListenersActive = true;
1348
+ document.addEventListener("scroll", repositionOpenFlyouts, true);
1349
+ window.addEventListener("resize", repositionOpenFlyouts);
1350
+ };
1351
+ const detachViewportListeners = () => {
1352
+ if (!viewportListenersActive || typeof document === "undefined") return;
1353
+ viewportListenersActive = false;
1354
+ document.removeEventListener("scroll", repositionOpenFlyouts, true);
1355
+ window.removeEventListener("resize", repositionOpenFlyouts);
1356
+ };
1357
+ vue.watch(
1358
+ shownFlyoutKeys,
1359
+ (keys, prev) => {
1360
+ if (keys.length > 0 && prev.length === 0) attachViewportListeners();
1361
+ else if (keys.length === 0 && prev.length > 0) detachViewportListeners();
1362
+ for (const key of keys) positionFlyout(key);
1363
+ },
1364
+ { flush: "post" }
1365
+ );
1366
+ return {
1367
+ hovered,
1368
+ hoveredBranches,
1369
+ focusedBranches,
1370
+ clickedBranches,
1371
+ select,
1372
+ toggleFlyout,
1373
+ openFlyout,
1374
+ closeHorizontalMenus,
1375
+ setBranchInteraction,
1376
+ keepsPointerInside,
1377
+ interactionKeyAtDepth,
1378
+ horizontalBranchVisible,
1379
+ shownFlyoutKeys,
1380
+ setFlyoutRef,
1381
+ flyoutTriggers,
1382
+ flyoutPanels,
1383
+ suppressFocusOpen: { get: () => suppressFocusOpen },
1384
+ setSuppressFocusOpen: (value) => {
1385
+ suppressFocusOpen = value;
1386
+ },
1387
+ cancelClose,
1388
+ scheduleOpen,
1389
+ scheduleClose,
1390
+ cancelAllTimers
1391
+ };
1392
+ }
1393
+ function createNavMenuKeydownHandler(deps) {
1394
+ const {
1395
+ items,
1396
+ flyoutMode,
1397
+ expanded,
1398
+ toggle,
1399
+ openFlyout,
1400
+ closeHorizontalMenus,
1401
+ horizontalBranchVisible,
1402
+ setBranchInteraction,
1403
+ focusedBranches,
1404
+ collapsed,
1405
+ hoveredBranches,
1406
+ clickedBranches,
1407
+ setSuppressFocusOpen
1408
+ } = deps;
1409
+ return (e) => {
1410
+ const root = e.currentTarget;
1411
+ const buttons = Array.from(root.querySelectorAll("[data-iris-nav-item]")).filter(
1412
+ (button) => !button.closest('[data-iris-nav-children][aria-hidden="true"]')
1413
+ );
1414
+ if (buttons.length === 0) return;
1415
+ const idx = buttons.indexOf(document.activeElement);
1416
+ const focusAt = (i) => buttons[(i + buttons.length) % buttons.length]?.focus();
1417
+ const key = idx >= 0 ? buttons[idx]?.getAttribute("data-key") : void 0;
1418
+ const node = key ? core.findNavNode(items, key) : void 0;
1419
+ const depth = idx >= 0 ? Number(buttons[idx]?.getAttribute("data-depth") ?? 0) : 0;
1420
+ switch (e.key) {
1421
+ case "ArrowDown": {
1422
+ e.preventDefault();
1423
+ if (flyoutMode() && node && core.isBranch(node)) {
1424
+ openFlyout(key);
1425
+ const group = buttons[idx].closest("[data-iris-nav-group]");
1426
+ void vue.nextTick(() => {
1427
+ group?.querySelector("[data-iris-nav-children] [data-iris-nav-item]")?.focus();
1428
+ });
1429
+ return;
1430
+ }
1431
+ focusAt(idx < 0 ? 0 : idx + 1);
1432
+ return;
1433
+ }
1434
+ case "ArrowUp": {
1435
+ e.preventDefault();
1436
+ if (flyoutMode() && node && core.isBranch(node) && horizontalBranchVisible(key, depth)) {
1437
+ setBranchInteraction(focusedBranches, key, false);
1438
+ setBranchInteraction(clickedBranches, key, false);
1439
+ return;
1440
+ }
1441
+ focusAt(idx < 0 ? buttons.length - 1 : idx - 1);
1442
+ return;
1443
+ }
1444
+ case "Home":
1445
+ e.preventDefault();
1446
+ buttons[0]?.focus();
1447
+ return;
1448
+ case "End":
1449
+ e.preventDefault();
1450
+ buttons[buttons.length - 1]?.focus();
1451
+ return;
1452
+ case "Escape": {
1453
+ if (flyoutMode()) {
1454
+ const openKeys = [
1455
+ ...hoveredBranches.value,
1456
+ ...clickedBranches.value,
1457
+ ...focusedBranches.value
1458
+ ];
1459
+ if (openKeys.length > 0) {
1460
+ e.preventDefault();
1461
+ const openTop = openKeys.find((k) => core.findNavPath(items, k).length === 1);
1462
+ closeHorizontalMenus();
1463
+ if (openTop) {
1464
+ setSuppressFocusOpen(true);
1465
+ buttons.find((b) => b.getAttribute("data-key") === openTop)?.focus();
1466
+ void vue.nextTick(() => {
1467
+ setSuppressFocusOpen(false);
1468
+ });
1469
+ }
1470
+ }
1471
+ }
1472
+ return;
1473
+ }
1474
+ }
1475
+ if (collapsed || idx < 0 || !key || !node) return;
1476
+ if (e.key === "ArrowRight") {
1477
+ e.preventDefault();
1478
+ if (core.isBranch(node)) {
1479
+ if (flyoutMode() || !expanded().includes(key)) {
1480
+ if (flyoutMode()) openFlyout(key);
1481
+ else toggle(key);
1482
+ const group = buttons[idx].closest("[data-iris-nav-group]");
1483
+ void vue.nextTick(() => {
1484
+ group?.querySelector("[data-iris-nav-children] [data-iris-nav-item]")?.focus();
1485
+ });
1486
+ } else {
1487
+ focusAt(idx + 1);
1488
+ }
1489
+ }
1490
+ } else if (e.key === "ArrowLeft") {
1491
+ e.preventDefault();
1492
+ if (flyoutMode() && core.isBranch(node)) {
1493
+ setBranchInteraction(focusedBranches, key, false);
1494
+ setBranchInteraction(clickedBranches, key, false);
1495
+ return;
1496
+ }
1497
+ if (core.isBranch(node) && expanded().includes(key)) {
1498
+ toggle(key);
1499
+ } else {
1500
+ const parentKey = core.findNavPath(items, key).at(-2)?.key;
1501
+ if (parentKey) {
1502
+ buttons.find((b) => b.getAttribute("data-key") === parentKey)?.focus();
1503
+ }
1504
+ }
1505
+ }
1506
+ };
1507
+ }
1191
1508
 
1192
1509
  // src/admin/NavMenu.ts
1193
- var HOVER_OPEN_DELAY = 100;
1194
- var HOVER_CLOSE_DELAY = 150;
1195
1510
  var IrisNavMenu = vue.defineComponent({
1196
1511
  name: "IrisNavMenu",
1197
1512
  inheritAttrs: false,
@@ -1253,170 +1568,35 @@ var IrisNavMenu = vue.defineComponent({
1253
1568
  emit("update:expandedKeys", model.get());
1254
1569
  }
1255
1570
  };
1256
- const hovered = vue.ref(null);
1257
- const hoveredBranches = vue.ref([]);
1258
- const focusedBranches = vue.ref([]);
1259
- const clickedBranches = vue.ref([]);
1260
- let suppressFocusOpen = false;
1261
- let openTimer = null;
1262
- const closeTimers = /* @__PURE__ */ new Map();
1263
- const clearOpenTimer = () => {
1264
- if (openTimer) {
1265
- clearTimeout(openTimer);
1266
- openTimer = null;
1267
- }
1268
- };
1269
- const cancelClose = (key) => {
1270
- const timer = closeTimers.get(key);
1271
- if (timer) {
1272
- clearTimeout(timer);
1273
- closeTimers.delete(key);
1274
- }
1275
- };
1276
- const cancelAllTimers = () => {
1277
- clearOpenTimer();
1278
- for (const timer of closeTimers.values()) clearTimeout(timer);
1279
- closeTimers.clear();
1280
- };
1281
- vue.onBeforeUnmount(cancelAllTimers);
1282
- const flyoutTriggers = /* @__PURE__ */ new Map();
1283
- const flyoutPanels = /* @__PURE__ */ new Map();
1284
- const setFlyoutRef = (map, key, el) => {
1285
- if (el instanceof HTMLElement) map.set(key, el);
1286
- else map.delete(key);
1287
- };
1288
- const positionFlyout = (key) => {
1289
- const trigger = flyoutTriggers.get(key);
1290
- const panel = flyoutPanels.get(key);
1291
- if (!trigger || !panel || typeof document === "undefined") return;
1292
- const rect = trigger.getBoundingClientRect();
1293
- const dir = getComputedStyle(trigger).direction;
1294
- panel.style.position = "fixed";
1295
- panel.style.top = `${props.collapsed ? rect.top : rect.bottom}px`;
1296
- if (dir === "rtl") {
1297
- panel.style.left = "";
1298
- panel.style.right = `${window.innerWidth - (props.collapsed ? rect.left : rect.right)}px`;
1299
- } else {
1300
- panel.style.right = "";
1301
- panel.style.left = `${props.collapsed ? rect.right : rect.left}px`;
1302
- }
1303
- };
1304
- vue.onBeforeUnmount(() => {
1305
- detachViewportListeners();
1306
- flyoutTriggers.clear();
1307
- flyoutPanels.clear();
1308
- });
1309
- const isolateAtDepth = (key) => {
1310
- const depth = core.findNavPath(props.items, key).length - 1;
1311
- const isSameDepthOther = (k) => k !== key && core.findNavPath(props.items, k).length - 1 === depth;
1312
- for (const state of [hoveredBranches, clickedBranches, focusedBranches]) {
1313
- if (state.value.some(isSameDepthOther)) {
1314
- state.value = state.value.filter((k) => !isSameDepthOther(k));
1315
- }
1316
- }
1317
- };
1318
- const scheduleOpen = (key) => {
1319
- clearOpenTimer();
1320
- openTimer = setTimeout(() => {
1321
- openTimer = null;
1322
- isolateAtDepth(key);
1323
- setBranchInteraction(hoveredBranches, key, true);
1324
- }, HOVER_OPEN_DELAY);
1325
- };
1326
- const scheduleClose = (key) => {
1327
- cancelClose(key);
1328
- closeTimers.set(
1329
- key,
1330
- setTimeout(() => {
1331
- closeTimers.delete(key);
1332
- setBranchInteraction(hoveredBranches, key, false);
1333
- }, HOVER_CLOSE_DELAY)
1334
- );
1335
- };
1336
- const closeHorizontalMenus = () => {
1337
- if (!flyoutMode.value) return;
1338
- cancelAllTimers();
1339
- hovered.value = null;
1340
- hoveredBranches.value = [];
1341
- focusedBranches.value = [];
1342
- clickedBranches.value = [];
1343
- };
1344
- const select = (node) => {
1345
- if (node.disabled) return;
1346
- closeHorizontalMenus();
1347
- emit("select", node.key, node);
1348
- };
1349
- const toggleFlyout = (key) => {
1350
- cancelAllTimers();
1351
- const current = clickedBranches.value;
1352
- if (current.includes(key)) {
1353
- clickedBranches.value = current.filter((k) => k !== key);
1354
- setBranchInteraction(hoveredBranches, key, false);
1355
- setBranchInteraction(focusedBranches, key, false);
1356
- } else {
1357
- isolateAtDepth(key);
1358
- clickedBranches.value = [...clickedBranches.value, key];
1359
- }
1360
- };
1361
- const openFlyout = (key) => {
1362
- cancelAllTimers();
1363
- isolateAtDepth(key);
1364
- setBranchInteraction(focusedBranches, key, true);
1365
- };
1366
- const setBranchInteraction = (state, key, enabled) => {
1367
- const current = state.value;
1368
- if (enabled) {
1369
- if (!current.includes(key)) state.value = [...current, key];
1370
- } else if (current.includes(key)) {
1371
- state.value = current.filter((item) => item !== key);
1372
- }
1373
- };
1374
- const keepsPointerInside = (container, event) => {
1375
- if (!container || !(container instanceof HTMLElement)) return false;
1376
- const next = event.relatedTarget;
1377
- return !!(next && next instanceof Node && container.contains(next));
1378
- };
1379
- const interactionKeyAtDepth = (keys, depth) => keys.find((key) => core.findNavPath(props.items, key).length === depth + 1);
1380
- const horizontalBranchVisible = (key, depth) => {
1381
- const hoveredAtDepth = interactionKeyAtDepth(hoveredBranches.value, depth);
1382
- if (hoveredAtDepth) return hoveredAtDepth === key;
1383
- const clickedAtDepth = interactionKeyAtDepth(clickedBranches.value, depth);
1384
- if (clickedAtDepth) return clickedAtDepth === key;
1385
- return interactionKeyAtDepth(focusedBranches.value, depth) === key;
1386
- };
1387
- const shownFlyoutKeys = vue.computed(() => {
1388
- if (!flyoutMode.value) return [];
1389
- const keys = [];
1390
- for (const node of tree.value) {
1391
- if (core.isBranch(node) && horizontalBranchVisible(node.key, 0)) keys.push(node.key);
1392
- }
1393
- return keys;
1571
+ const flyout = createNavMenuFlyout({
1572
+ items: props.items,
1573
+ tree,
1574
+ expanded,
1575
+ flyoutMode: { value: flyoutMode.value },
1576
+ collapsed: props.collapsed,
1577
+ toggle,
1578
+ emitSelect: (key, node) => emit("select", key, node)
1394
1579
  });
1395
- const repositionOpenFlyouts = () => {
1396
- for (const key of shownFlyoutKeys.value) positionFlyout(key);
1397
- };
1398
- let viewportListenersActive = false;
1399
- const attachViewportListeners = () => {
1400
- if (viewportListenersActive || typeof document === "undefined") return;
1401
- viewportListenersActive = true;
1402
- document.addEventListener("scroll", repositionOpenFlyouts, true);
1403
- window.addEventListener("resize", repositionOpenFlyouts);
1404
- };
1405
- const detachViewportListeners = () => {
1406
- if (!viewportListenersActive || typeof document === "undefined") return;
1407
- viewportListenersActive = false;
1408
- document.removeEventListener("scroll", repositionOpenFlyouts, true);
1409
- window.removeEventListener("resize", repositionOpenFlyouts);
1410
- };
1411
- vue.watch(
1412
- shownFlyoutKeys,
1413
- (keys, prev) => {
1414
- if (keys.length > 0 && prev.length === 0) attachViewportListeners();
1415
- else if (keys.length === 0 && prev.length > 0) detachViewportListeners();
1416
- for (const key of keys) positionFlyout(key);
1417
- },
1418
- { flush: "post" }
1419
- );
1580
+ const {
1581
+ hovered,
1582
+ hoveredBranches,
1583
+ focusedBranches,
1584
+ clickedBranches,
1585
+ select,
1586
+ toggleFlyout,
1587
+ openFlyout,
1588
+ closeHorizontalMenus,
1589
+ setBranchInteraction,
1590
+ keepsPointerInside,
1591
+ horizontalBranchVisible,
1592
+ setFlyoutRef,
1593
+ flyoutTriggers,
1594
+ flyoutPanels,
1595
+ suppressFocusOpen,
1596
+ cancelClose,
1597
+ scheduleOpen,
1598
+ scheduleClose
1599
+ } = flyout;
1420
1600
  const itemClass = (opts) => {
1421
1601
  const depth = Math.min(9, opts.depth);
1422
1602
  return [
@@ -1552,7 +1732,7 @@ var IrisNavMenu = vue.defineComponent({
1552
1732
  }
1553
1733
  },
1554
1734
  onFocusin: () => {
1555
- if (suppressFocusOpen) return;
1735
+ if (suppressFocusOpen.get()) return;
1556
1736
  setBranchInteraction(focusedBranches, node.key, true);
1557
1737
  },
1558
1738
  onFocusout: (event) => {
@@ -1605,104 +1785,23 @@ var IrisNavMenu = vue.defineComponent({
1605
1785
  },
1606
1786
  { flush: "post" }
1607
1787
  );
1608
- const onKeydown = (e) => {
1609
- const root = e.currentTarget;
1610
- const buttons = Array.from(root.querySelectorAll("[data-iris-nav-item]")).filter(
1611
- (button) => !button.closest('[data-iris-nav-children][aria-hidden="true"]')
1612
- );
1613
- if (buttons.length === 0) return;
1614
- const idx = buttons.indexOf(document.activeElement);
1615
- const focusAt = (i) => buttons[(i + buttons.length) % buttons.length]?.focus();
1616
- const key = idx >= 0 ? buttons[idx]?.getAttribute("data-key") : void 0;
1617
- const node = key ? core.findNavNode(props.items, key) : void 0;
1618
- const depth = idx >= 0 ? Number(buttons[idx]?.getAttribute("data-depth") ?? 0) : 0;
1619
- switch (e.key) {
1620
- case "ArrowDown": {
1621
- e.preventDefault();
1622
- if (flyoutMode.value && node && core.isBranch(node)) {
1623
- openFlyout(key);
1624
- const group = buttons[idx].closest("[data-iris-nav-group]");
1625
- void vue.nextTick(() => {
1626
- group?.querySelector("[data-iris-nav-children] [data-iris-nav-item]")?.focus();
1627
- });
1628
- return;
1629
- }
1630
- focusAt(idx < 0 ? 0 : idx + 1);
1631
- return;
1632
- }
1633
- case "ArrowUp": {
1634
- e.preventDefault();
1635
- if (flyoutMode.value && node && core.isBranch(node) && horizontalBranchVisible(key, depth)) {
1636
- setBranchInteraction(focusedBranches, key, false);
1637
- setBranchInteraction(clickedBranches, key, false);
1638
- return;
1639
- }
1640
- focusAt(idx < 0 ? buttons.length - 1 : idx - 1);
1641
- return;
1642
- }
1643
- case "Home":
1644
- e.preventDefault();
1645
- buttons[0]?.focus();
1646
- return;
1647
- case "End":
1648
- e.preventDefault();
1649
- buttons[buttons.length - 1]?.focus();
1650
- return;
1651
- case "Escape": {
1652
- if (flyoutMode.value) {
1653
- const openKeys = [
1654
- ...hoveredBranches.value,
1655
- ...clickedBranches.value,
1656
- ...focusedBranches.value
1657
- ];
1658
- if (openKeys.length > 0) {
1659
- e.preventDefault();
1660
- const openTop = openKeys.find((k) => core.findNavPath(props.items, k).length === 1);
1661
- closeHorizontalMenus();
1662
- if (openTop) {
1663
- suppressFocusOpen = true;
1664
- buttons.find((b) => b.getAttribute("data-key") === openTop)?.focus();
1665
- void vue.nextTick(() => {
1666
- suppressFocusOpen = false;
1667
- });
1668
- }
1669
- }
1670
- }
1671
- return;
1672
- }
1673
- }
1674
- if (props.collapsed || idx < 0 || !key || !node) return;
1675
- if (e.key === "ArrowRight") {
1676
- e.preventDefault();
1677
- if (core.isBranch(node)) {
1678
- if (flyoutMode.value || !expanded.value.includes(key)) {
1679
- if (flyoutMode.value) openFlyout(key);
1680
- else toggle(key);
1681
- const group = buttons[idx].closest("[data-iris-nav-group]");
1682
- void vue.nextTick(() => {
1683
- group?.querySelector("[data-iris-nav-children] [data-iris-nav-item]")?.focus();
1684
- });
1685
- } else {
1686
- focusAt(idx + 1);
1687
- }
1688
- }
1689
- } else if (e.key === "ArrowLeft") {
1690
- e.preventDefault();
1691
- if (flyoutMode.value && core.isBranch(node)) {
1692
- setBranchInteraction(focusedBranches, key, false);
1693
- setBranchInteraction(clickedBranches, key, false);
1694
- return;
1695
- }
1696
- if (core.isBranch(node) && expanded.value.includes(key)) {
1697
- toggle(key);
1698
- } else {
1699
- const parentKey = core.findNavPath(props.items, key).at(-2)?.key;
1700
- if (parentKey) {
1701
- buttons.find((b) => b.getAttribute("data-key") === parentKey)?.focus();
1702
- }
1703
- }
1704
- }
1705
- };
1788
+ const onKeydown = createNavMenuKeydownHandler({
1789
+ items: props.items,
1790
+ flyoutMode: () => flyoutMode.value,
1791
+ expanded: () => expanded.value,
1792
+ toggle,
1793
+ openFlyout,
1794
+ closeHorizontalMenus,
1795
+ horizontalBranchVisible,
1796
+ setBranchInteraction,
1797
+ focusedBranches,
1798
+ hoveredBranches,
1799
+ clickedBranches,
1800
+ setSuppressFocusOpen: (v) => {
1801
+ flyout.setSuppressFocusOpen(v);
1802
+ },
1803
+ collapsed: props.collapsed
1804
+ });
1706
1805
  const menuClass = [
1707
1806
  "iris-nav-menu",
1708
1807
  `iris-nav-menu--${props.orientation}`,
@@ -12414,8 +12513,8 @@ var IrisMenuSeparator = vue.defineComponent({
12414
12513
  });
12415
12514
  }
12416
12515
  });
12417
- var HOVER_OPEN_DELAY2 = 100;
12418
- var HOVER_CLOSE_DELAY2 = 150;
12516
+ var HOVER_OPEN_DELAY = 100;
12517
+ var HOVER_CLOSE_DELAY = 150;
12419
12518
  var IrisMenuSub = vue.defineComponent({
12420
12519
  name: "IrisMenuSub",
12421
12520
  inheritAttrs: false,
@@ -12456,7 +12555,7 @@ var IrisMenuSub = vue.defineComponent({
12456
12555
  openTimer = setTimeout(() => {
12457
12556
  open.value = true;
12458
12557
  openTimer = null;
12459
- }, HOVER_OPEN_DELAY2);
12558
+ }, HOVER_OPEN_DELAY);
12460
12559
  };
12461
12560
  const scheduleClose = () => {
12462
12561
  clearTimer();
@@ -12465,7 +12564,7 @@ var IrisMenuSub = vue.defineComponent({
12465
12564
  closeTimer = null;
12466
12565
  keyboardManaged = false;
12467
12566
  open.value = false;
12468
- }, HOVER_CLOSE_DELAY2);
12567
+ }, HOVER_CLOSE_DELAY);
12469
12568
  };
12470
12569
  vue.onScopeDispose(() => {
12471
12570
  clearTimer();