@iris-ui-kit/react 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/admin.cjs CHANGED
@@ -266,11 +266,13 @@ function IrisNavMenu({
266
266
  expandedKeys,
267
267
  defaultExpandedKeys,
268
268
  collapsed = false,
269
+ orientation = "vertical",
269
270
  ariaLabel,
270
271
  onSelect,
271
272
  onExpandedKeysChange
272
273
  }) {
273
274
  const { t } = useI18n();
275
+ const horizontal = orientation === "horizontal";
274
276
  const trailOf = React13__namespace.useCallback(
275
277
  (key) => key ? core.branchTrail(items, key) : [],
276
278
  [items]
@@ -306,7 +308,7 @@ function IrisNavMenu({
306
308
  display: "flex",
307
309
  alignItems: "center",
308
310
  gap: 10,
309
- width: "100%",
311
+ width: horizontal && opts.depth === 0 ? "auto" : "100%",
310
312
  boxSizing: "border-box",
311
313
  border: "none",
312
314
  borderRadius: "var(--iris-radius-md, 6px)",
@@ -318,7 +320,8 @@ function IrisNavMenu({
318
320
  textAlign: "start",
319
321
  cursor: opts.disabled ? "not-allowed" : "pointer",
320
322
  opacity: opts.disabled ? 0.5 : 1,
321
- padding: collapsed ? 10 : `8px 10px 8px ${12 + opts.depth * 16}px`,
323
+ padding: collapsed ? 10 : "8px 10px",
324
+ paddingInlineStart: collapsed ? 10 : 12 + opts.depth * 16,
322
325
  justifyContent: collapsed ? "center" : "flex-start"
323
326
  });
324
327
  const iconNode = (node, size = 18) => node.icon ? /* @__PURE__ */ jsxRuntime.jsx(IrisIcon, { name: node.icon, size }) : null;
@@ -425,12 +428,48 @@ function IrisNavMenu({
425
428
  }
426
429
  );
427
430
  if (!branch || !open) {
428
- return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-iris-nav-group": branch ? "" : void 0, children: row }, node.key);
431
+ return /* @__PURE__ */ jsxRuntime.jsx(
432
+ "div",
433
+ {
434
+ "data-iris-nav-group": branch ? "" : void 0,
435
+ style: { position: horizontal && depth === 0 ? "relative" : void 0 },
436
+ children: row
437
+ },
438
+ node.key
439
+ );
429
440
  }
430
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-iris-nav-group": "", "data-open": "true", children: [
431
- row,
432
- /* @__PURE__ */ jsxRuntime.jsx("div", { "data-iris-nav-children": "", role: "group", children: (node.children ?? []).map((child) => renderItem(child, depth + 1)) })
433
- ] }, node.key);
441
+ return /* @__PURE__ */ jsxRuntime.jsxs(
442
+ "div",
443
+ {
444
+ "data-iris-nav-group": "",
445
+ "data-open": "true",
446
+ style: { position: horizontal && depth === 0 ? "relative" : void 0 },
447
+ children: [
448
+ row,
449
+ /* @__PURE__ */ jsxRuntime.jsx(
450
+ "div",
451
+ {
452
+ "data-iris-nav-children": "",
453
+ role: "group",
454
+ style: horizontal && depth === 0 ? {
455
+ position: "absolute",
456
+ insetBlockStart: "calc(100% + 4px)",
457
+ insetInlineStart: 0,
458
+ zIndex: 60,
459
+ minWidth: 220,
460
+ padding: 6,
461
+ border: "1px solid var(--iris-border)",
462
+ borderRadius: "var(--iris-radius-md, 6px)",
463
+ background: "var(--iris-surface)",
464
+ boxShadow: "var(--iris-shadow-md)"
465
+ } : void 0,
466
+ children: (node.children ?? []).map((child) => renderItem(child, depth + 1))
467
+ }
468
+ )
469
+ ]
470
+ },
471
+ node.key
472
+ );
434
473
  };
435
474
  const onKeyDown = (e) => {
436
475
  const root = e.currentTarget;
@@ -478,9 +517,15 @@ function IrisNavMenu({
478
517
  {
479
518
  "data-iris-nav-menu": "",
480
519
  "data-collapsed": collapsed ? "true" : void 0,
520
+ "data-orientation": orientation,
481
521
  "aria-label": ariaLabel ?? t("admin.nav"),
482
522
  onKeyDown,
483
- style: { display: "flex", flexDirection: "column", gap: 2 },
523
+ style: {
524
+ display: "flex",
525
+ flexDirection: horizontal ? "row" : "column",
526
+ alignItems: horizontal ? "center" : void 0,
527
+ gap: 2
528
+ },
484
529
  children: collapsed ? tree.map((node) => renderCollapsed(node)) : tree.map((node) => renderItem(node, 0))
485
530
  }
486
531
  );
@@ -611,6 +656,7 @@ function useTabsNav(nav) {
611
656
  closeRight: nav.closeRight,
612
657
  refresh: nav.refresh,
613
658
  setPinned: nav.setPinned,
659
+ move: nav.move,
614
660
  cacheKey: nav.cacheKey
615
661
  };
616
662
  }
@@ -1109,11 +1155,110 @@ var IrisDropdownSeparator = React13__namespace.forwardRef(
1109
1155
  );
1110
1156
  }
1111
1157
  );
1158
+ var SORTABLE_ITEM_ATTR = "data-iris-sortable-item";
1159
+ function IrisSortable({
1160
+ items,
1161
+ onReorder,
1162
+ getKey = (_item, index) => String(index),
1163
+ disabled = false,
1164
+ orientation = "vertical",
1165
+ style,
1166
+ className,
1167
+ children,
1168
+ ...rest
1169
+ }) {
1170
+ const containerRef = React13__namespace.useRef(null);
1171
+ const ctrlRef = React13__namespace.useRef(null);
1172
+ if (ctrlRef.current === null) ctrlRef.current = core.createSortable();
1173
+ const ctrl = ctrlRef.current;
1174
+ const [state, setState] = React13__namespace.useState({ activeId: null, overId: null });
1175
+ React13__namespace.useEffect(() => ctrl.subscribe(setState), [ctrl]);
1176
+ const dragging = state.activeId;
1177
+ const indexByKey = (key) => Array.from(containerRef.current?.querySelectorAll(`[${SORTABLE_ITEM_ATTR}]`) ?? []).findIndex(
1178
+ (el) => el.getAttribute(SORTABLE_ITEM_ATTR) === key
1179
+ );
1180
+ const onPointerDown = (e) => {
1181
+ if (disabled) return;
1182
+ const target = e.target.closest(`[${SORTABLE_ITEM_ATTR}]`);
1183
+ if (!target) return;
1184
+ const key = target.getAttribute(SORTABLE_ITEM_ATTR) ?? "";
1185
+ if (!key) return;
1186
+ ctrl.press(key, e.clientX, e.clientY);
1187
+ };
1188
+ const onPointerMove = (e) => {
1189
+ if (!ctrl.getState().activeId) {
1190
+ if (!ctrl.tryStart(e.clientX, e.clientY)) return;
1191
+ }
1192
+ const els = containerRef.current?.querySelectorAll(`[${SORTABLE_ITEM_ATTR}]`);
1193
+ const rects = Array.from(els ?? []).map((el) => ({
1194
+ id: el.getAttribute(SORTABLE_ITEM_ATTR) ?? "",
1195
+ ...el.getBoundingClientRect()
1196
+ }));
1197
+ ctrl.moveOver({ x: e.clientX, y: e.clientY }, rects);
1198
+ };
1199
+ const onPointerUp = () => {
1200
+ const result = ctrl.end();
1201
+ if (result.activeId && result.overId && result.activeId !== result.overId) {
1202
+ const from = indexByKey(result.activeId);
1203
+ const to = indexByKey(result.overId);
1204
+ if (from >= 0 && to >= 0 && from !== to) {
1205
+ const next = [...items];
1206
+ const [moved] = next.splice(from, 1);
1207
+ next.splice(to, 0, moved);
1208
+ onReorder(next);
1209
+ }
1210
+ }
1211
+ };
1212
+ return /* @__PURE__ */ jsxRuntime.jsx(
1213
+ "div",
1214
+ {
1215
+ ...rest,
1216
+ ref: containerRef,
1217
+ "data-iris-sortable": "",
1218
+ "data-state": dragging ? "dragging" : "idle",
1219
+ className,
1220
+ style: {
1221
+ display: "flex",
1222
+ flexDirection: orientation === "horizontal" ? "row" : "column",
1223
+ gap: "var(--iris-gap-sm, 4px)",
1224
+ opacity: disabled ? 0.6 : 1,
1225
+ userSelect: dragging ? "none" : void 0,
1226
+ ...style
1227
+ },
1228
+ onPointerDown,
1229
+ onPointerMove,
1230
+ onPointerUp,
1231
+ onPointerLeave: onPointerUp,
1232
+ children: React13__namespace.Children.map(children, (child, index) => {
1233
+ if (!React13__namespace.isValidElement(child)) return child;
1234
+ const key = getKey(items[index], index);
1235
+ const isDragging = key === dragging;
1236
+ return /* @__PURE__ */ jsxRuntime.jsx(
1237
+ "div",
1238
+ {
1239
+ "data-iris-sortable-item": key,
1240
+ "data-iris-sortable-dragging": isDragging ? "" : void 0,
1241
+ style: {
1242
+ transition: isDragging ? "none" : "transform 150ms ease",
1243
+ opacity: isDragging ? 0.4 : 1,
1244
+ position: "relative",
1245
+ zIndex: isDragging ? 100 : void 0
1246
+ },
1247
+ children: child
1248
+ },
1249
+ key
1250
+ );
1251
+ })
1252
+ }
1253
+ );
1254
+ }
1112
1255
  function IrisAdminTabs({
1113
1256
  nav,
1114
1257
  onChange,
1115
1258
  onClose,
1116
- onRefresh
1259
+ onRefresh,
1260
+ reorderable = true,
1261
+ onReorder
1117
1262
  }) {
1118
1263
  const t = useTabsNav(nav);
1119
1264
  const { t: translate } = useI18n();
@@ -1139,6 +1284,10 @@ function IrisAdminTabs({
1139
1284
  nav.refresh(key);
1140
1285
  onRefresh?.(key);
1141
1286
  };
1287
+ const reorder = (tabs) => {
1288
+ tabs.forEach((tab, index) => nav.move(tab.key, index));
1289
+ onReorder?.(tabs);
1290
+ };
1142
1291
  const onKeyDown = (e) => {
1143
1292
  const tabs = t.tabs;
1144
1293
  const idx = tabs.findIndex((x) => x.key === t.activeKey);
@@ -1270,8 +1419,19 @@ function IrisAdminTabs({
1270
1419
  role: "tablist",
1271
1420
  "aria-label": translate("admin.openPages"),
1272
1421
  onKeyDown,
1273
- style: { display: "flex", alignItems: "center", gap: 6, overflowX: "auto", flex: 1 },
1274
- children: t.tabs.map((tab) => chip(tab))
1422
+ style: { overflowX: "auto", flex: 1 },
1423
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1424
+ IrisSortable,
1425
+ {
1426
+ items: t.tabs,
1427
+ getKey: (tab) => tab.key,
1428
+ onReorder: reorder,
1429
+ disabled: !reorderable,
1430
+ orientation: "horizontal",
1431
+ style: { alignItems: "center", gap: 6, width: "max-content" },
1432
+ children: t.tabs.map((tab) => chip(tab))
1433
+ }
1434
+ )
1275
1435
  }
1276
1436
  ),
1277
1437
  /* @__PURE__ */ jsxRuntime.jsxs(IrisDropdown, { children: [
@@ -1298,6 +1458,8 @@ function IrisAdminTabs({
1298
1458
  /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownItem, { onSelect: () => refresh(activeKey), children: translate("admin.refresh") }),
1299
1459
  /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownItem, { onSelect: () => close(activeKey), children: translate("admin.close") }),
1300
1460
  /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownSeparator, {}),
1461
+ /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownItem, { onSelect: () => nav.closeLeft(activeKey), children: translate("admin.closeLeft") }),
1462
+ /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownItem, { onSelect: () => nav.closeRight(activeKey), children: translate("admin.closeRight") }),
1301
1463
  /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownItem, { onSelect: () => nav.closeOthers(activeKey), children: translate("admin.closeOthers") }),
1302
1464
  /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownItem, { onSelect: () => nav.closeAll(), children: translate("admin.closeAll") })
1303
1465
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(IrisDropdownItem, { disabled: true, children: "No active tab" }) })
@@ -1363,7 +1525,13 @@ function IrisAdminLayout({
1363
1525
  mode = "sidebar",
1364
1526
  appTitle = "Iris Admin",
1365
1527
  tabs,
1528
+ showTabs = true,
1366
1529
  showBreadcrumb = true,
1530
+ stickyHeader = true,
1531
+ stickyTabs = true,
1532
+ menuAlign = "start",
1533
+ contentWidth = "fluid",
1534
+ contentHeight = "viewport",
1367
1535
  sidebarWidth = 240,
1368
1536
  collapsedWidth = 64,
1369
1537
  logo,
@@ -1434,6 +1602,37 @@ function IrisAdminLayout({
1434
1602
  }
1435
1603
  );
1436
1604
  const renderLogo = (state) => typeof logo === "function" ? logo(state) : logo ?? defaultLogo(state);
1605
+ const tabsBar = tabs && showTabs ? /* @__PURE__ */ jsxRuntime.jsx(
1606
+ "div",
1607
+ {
1608
+ "data-iris-admin-tabs-region": "",
1609
+ "data-sticky": stickyTabs ? "true" : void 0,
1610
+ style: {
1611
+ position: stickyTabs && !stickyHeader ? "sticky" : "relative",
1612
+ top: 0,
1613
+ zIndex: 49
1614
+ },
1615
+ children: /* @__PURE__ */ jsxRuntime.jsx(IrisAdminTabs, { nav: tabs })
1616
+ }
1617
+ ) : null;
1618
+ const contentRegion = /* @__PURE__ */ jsxRuntime.jsxs(
1619
+ "div",
1620
+ {
1621
+ "data-iris-admin-content": "",
1622
+ "data-width": contentWidth,
1623
+ style: {
1624
+ boxSizing: "border-box",
1625
+ width: "100%",
1626
+ maxWidth: contentWidth === "centered" ? "72rem" : void 0,
1627
+ marginInline: contentWidth === "centered" ? "auto" : void 0,
1628
+ padding: 16
1629
+ },
1630
+ children: [
1631
+ mode === "horizontal" && showBreadcrumb ? /* @__PURE__ */ jsxRuntime.jsx(IrisAdminBreadcrumb, { trail, onSelect: handleSelect }) : null,
1632
+ renderContent()
1633
+ ]
1634
+ }
1635
+ );
1437
1636
  const collapseToggle = /* @__PURE__ */ jsxRuntime.jsx(
1438
1637
  "button",
1439
1638
  {
@@ -1479,6 +1678,75 @@ function IrisAdminLayout({
1479
1678
  ]
1480
1679
  }
1481
1680
  );
1681
+ if (mode === "horizontal") {
1682
+ const justifyContent = menuAlign === "center" ? "center" : menuAlign === "end" ? "flex-end" : "flex-start";
1683
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1684
+ "div",
1685
+ {
1686
+ "data-iris-admin-layout": "",
1687
+ "data-mode": "horizontal",
1688
+ style: {
1689
+ height: contentHeight === "viewport" ? "100vh" : "auto",
1690
+ minHeight: "100vh"
1691
+ },
1692
+ children: [
1693
+ tabs ? /* @__PURE__ */ jsxRuntime.jsx(TabsSync, { nav: tabs, activeKey: currentActive, onActivate: syncFromTab }) : null,
1694
+ /* @__PURE__ */ jsxRuntime.jsx(
1695
+ IrisHeaderLayout,
1696
+ {
1697
+ sticky: stickyHeader,
1698
+ footer,
1699
+ header: /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-iris-admin-header": "", children: [
1700
+ /* @__PURE__ */ jsxRuntime.jsxs(
1701
+ "div",
1702
+ {
1703
+ "data-iris-admin-headerbar": "",
1704
+ style: {
1705
+ display: "flex",
1706
+ alignItems: "center",
1707
+ gap: 12,
1708
+ minHeight: 52,
1709
+ padding: "0 16px",
1710
+ background: "var(--iris-background)"
1711
+ },
1712
+ children: [
1713
+ renderLogo({ collapsed: false }),
1714
+ /* @__PURE__ */ jsxRuntime.jsx(
1715
+ "div",
1716
+ {
1717
+ "data-iris-admin-topnav": "",
1718
+ style: { display: "flex", flex: 1, minWidth: 0, justifyContent },
1719
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1720
+ IrisNavMenu,
1721
+ {
1722
+ items: menus,
1723
+ activeKey: currentActive,
1724
+ orientation: "horizontal",
1725
+ onSelect: handleSelect
1726
+ }
1727
+ )
1728
+ }
1729
+ ),
1730
+ toolbar ? /* @__PURE__ */ jsxRuntime.jsx(
1731
+ "div",
1732
+ {
1733
+ "data-iris-admin-toolbar": "",
1734
+ style: { display: "flex", alignItems: "center", gap: 8 },
1735
+ children: toolbar
1736
+ }
1737
+ ) : null
1738
+ ]
1739
+ }
1740
+ ),
1741
+ tabsBar
1742
+ ] }),
1743
+ children: contentRegion
1744
+ }
1745
+ )
1746
+ ]
1747
+ }
1748
+ );
1749
+ }
1482
1750
  return /* @__PURE__ */ jsxRuntime.jsxs(
1483
1751
  IrisSidebarLayout,
1484
1752
  {
@@ -1519,20 +1787,38 @@ function IrisAdminLayout({
1519
1787
  /* @__PURE__ */ jsxRuntime.jsx(
1520
1788
  IrisHeaderLayout,
1521
1789
  {
1522
- sticky: true,
1790
+ sticky: stickyHeader,
1523
1791
  header: /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-iris-admin-header": "", children: [
1524
1792
  headerBar,
1525
- tabs ? /* @__PURE__ */ jsxRuntime.jsx(IrisAdminTabs, { nav: tabs }) : null
1793
+ tabsBar
1526
1794
  ] }),
1527
1795
  footer,
1528
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { "data-iris-admin-content": "", style: { padding: 16 }, children: renderContent() })
1796
+ children: contentRegion
1529
1797
  }
1530
1798
  )
1531
1799
  ]
1532
1800
  }
1533
1801
  );
1534
1802
  }
1803
+ function useAdminPreferences(preferences, hydrate = true) {
1804
+ const state = useStore(preferences.store);
1805
+ React13__namespace.useEffect(() => {
1806
+ if (hydrate) void preferences.hydrate();
1807
+ }, [hydrate, preferences]);
1808
+ return {
1809
+ state,
1810
+ set: preferences.set,
1811
+ patch: preferences.patch,
1812
+ reset: preferences.reset,
1813
+ hydrate: preferences.hydrate,
1814
+ flush: preferences.flush
1815
+ };
1816
+ }
1535
1817
 
1818
+ Object.defineProperty(exports, "createAdminPreferences", {
1819
+ enumerable: true,
1820
+ get: function () { return core.createAdminPreferences; }
1821
+ });
1536
1822
  Object.defineProperty(exports, "createTabsNav", {
1537
1823
  enumerable: true,
1538
1824
  get: function () { return core.createTabsNav; }
@@ -1565,6 +1851,10 @@ Object.defineProperty(exports, "isClosable", {
1565
1851
  enumerable: true,
1566
1852
  get: function () { return core.isClosable; }
1567
1853
  });
1854
+ Object.defineProperty(exports, "localStorageAdminPreferencesStorage", {
1855
+ enumerable: true,
1856
+ get: function () { return core.localStorageAdminPreferencesStorage; }
1857
+ });
1568
1858
  Object.defineProperty(exports, "nodeAllowsRoles", {
1569
1859
  enumerable: true,
1570
1860
  get: function () { return core.nodeAllowsRoles; }
@@ -1577,6 +1867,7 @@ exports.IrisAdminBreadcrumb = IrisAdminBreadcrumb;
1577
1867
  exports.IrisAdminLayout = IrisAdminLayout;
1578
1868
  exports.IrisAdminTabs = IrisAdminTabs;
1579
1869
  exports.IrisNavMenu = IrisNavMenu;
1870
+ exports.useAdminPreferences = useAdminPreferences;
1580
1871
  exports.useAdminShell = useAdminShell;
1581
1872
  exports.useTabsNav = useTabsNav;
1582
1873
  //# sourceMappingURL=admin.cjs.map