@xsolla/xui-b2b-sidebar 0.150.0 → 0.152.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/web/index.d.ts CHANGED
@@ -61,6 +61,7 @@ type SidebarItemType = {
61
61
  visibility?: string;
62
62
  disallowedIntegrationTypes?: Array<string>;
63
63
  isPinned?: boolean;
64
+ onPinToggle?: (e: MouseEvent<HTMLButtonElement>) => void;
64
65
  showBadge?: boolean;
65
66
  };
66
67
 
@@ -119,6 +120,8 @@ declare const SidebarTrigger: React.FC;
119
120
  interface SidebarGroupProps {
120
121
  label?: string;
121
122
  dataId?: string;
123
+ /** Docks this group to the bottom of the expanded sidebar (e.g. Settings, Billing). */
124
+ pinnedToBottom?: boolean;
122
125
  children: ReactNode;
123
126
  }
124
127
  declare const SidebarGroup: React.FC<SidebarGroupProps>;
@@ -139,6 +142,10 @@ interface SidebarMenuItemProps {
139
142
  dataId?: string;
140
143
  isActive?: SidebarLinkActiveCheck;
141
144
  isPinned?: boolean;
145
+ /** When provided, renders a pin/unpin toggle on hover (or always, when pinned). */
146
+ onPinToggle?: (e: React.MouseEvent<HTMLButtonElement>) => void;
147
+ /** When true on a pinned item, the leading pin glyph swaps to a drag handle on row hover (reorder affordance). */
148
+ dragHandle?: boolean;
142
149
  showBadge?: boolean;
143
150
  hasTooltip?: boolean;
144
151
  beta?: boolean;
@@ -185,4 +192,42 @@ interface SidebarCollapsedProps {
185
192
  }
186
193
  declare const SidebarCollapsed: React.FC<SidebarCollapsedProps>;
187
194
 
188
- export { Sidebar, SidebarChatButton, SidebarCollapsed, type SidebarCollapsedProps, SidebarContent, SidebarFooter, SidebarGroup, type SidebarItemType, type SidebarLinkActiveCheck, type SidebarLinkClickHandler, type SidebarLinkProps, SidebarMenu, SidebarMenuCollapsible, type SidebarMenuCollapsibleProps, SidebarMenuItem, type SidebarMenuItemProps, SidebarMenuSub, type SidebarProps, SidebarProvider, type SidebarProviderProps, SidebarTrigger, useSidebar };
195
+ interface SidebarPinnedItem {
196
+ key: string;
197
+ to: string;
198
+ label: ReactNode;
199
+ exact?: boolean;
200
+ }
201
+ interface SidebarPinnedListProps {
202
+ /** Ordered list of currently-pinned items. */
203
+ items: SidebarPinnedItem[];
204
+ /** Called when the user clicks the × on a pinned shortcut. */
205
+ onUnpin: (key: string) => void;
206
+ /**
207
+ * Called as the user drags to reorder. Omit to disable drag-and-drop —
208
+ * the leading icon stays as the static pin glyph.
209
+ */
210
+ onReorder?: (fromKey: string, toKey: string) => void;
211
+ /** Render 8px top/bottom spacers when items are present. Default true. */
212
+ spacers?: boolean;
213
+ }
214
+ /**
215
+ * Renders the pinned shortcuts list with pointer-event-based drag-and-drop
216
+ * reorder. Drag only initiates from the leading drag-handle area on each row.
217
+ *
218
+ * Pinning is fully opt-in: consumers who don't render this component get no
219
+ * pin UI. Consumers without a reorder backend can omit `onReorder`; the list
220
+ * stays static but unpinning still works.
221
+ */
222
+ declare const SidebarPinnedList: React.FC<SidebarPinnedListProps>;
223
+ /**
224
+ * Helper for the collapsed sidebar: returns a single `SidebarItemType` whose
225
+ * popover lists every pinned shortcut. Spread it into `collapsedItems` at the
226
+ * position you want the Pin button to appear. Returns `null` when no items.
227
+ */
228
+ declare const getPinnedCollapsedItem: (items: SidebarPinnedItem[], options?: {
229
+ label?: string;
230
+ dataId?: string;
231
+ }) => SidebarItemType | null;
232
+
233
+ export { Sidebar, SidebarChatButton, SidebarCollapsed, type SidebarCollapsedProps, SidebarContent, SidebarFooter, SidebarGroup, type SidebarItemType, type SidebarLinkActiveCheck, type SidebarLinkClickHandler, type SidebarLinkProps, SidebarMenu, SidebarMenuCollapsible, type SidebarMenuCollapsibleProps, SidebarMenuItem, type SidebarMenuItemProps, SidebarMenuSub, type SidebarPinnedItem, SidebarPinnedList, type SidebarPinnedListProps, type SidebarProps, SidebarProvider, type SidebarProviderProps, SidebarTrigger, getPinnedCollapsedItem, useSidebar };
package/web/index.js CHANGED
@@ -40,8 +40,10 @@ __export(index_exports, {
40
40
  SidebarMenuCollapsible: () => SidebarMenuCollapsible,
41
41
  SidebarMenuItem: () => SidebarMenuItem,
42
42
  SidebarMenuSub: () => SidebarMenuSub,
43
+ SidebarPinnedList: () => SidebarPinnedList,
43
44
  SidebarProvider: () => SidebarProvider,
44
45
  SidebarTrigger: () => SidebarTrigger,
46
+ getPinnedCollapsedItem: () => getPinnedCollapsedItem,
45
47
  useSidebar: () => useSidebar
46
48
  });
47
49
  module.exports = __toCommonJS(index_exports);
@@ -1276,6 +1278,7 @@ var import_jsx_runtime9 = require("react/jsx-runtime");
1276
1278
  var SidebarGroup = ({
1277
1279
  label,
1278
1280
  dataId,
1281
+ pinnedToBottom,
1279
1282
  children
1280
1283
  }) => {
1281
1284
  const { theme } = (0, import_xui_core6.useResolvedTheme)();
@@ -1287,6 +1290,7 @@ var SidebarGroup = ({
1287
1290
  "data-id": dataId,
1288
1291
  role: label ? "group" : void 0,
1289
1292
  "aria-label": label,
1293
+ style: pinnedToBottom ? { marginTop: "auto" } : void 0,
1290
1294
  children: [
1291
1295
  label && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1292
1296
  Box,
@@ -1379,8 +1383,76 @@ var IconBox = import_styled_components6.default.div`
1379
1383
  color: inherit;
1380
1384
 
1381
1385
  svg {
1382
- width: ${(p) => p.$pin ? "12px" : `${p.$size}px`};
1383
- height: ${(p) => p.$pin ? "12px" : `${p.$size}px`};
1386
+ width: ${(p) => p.$size}px;
1387
+ height: ${(p) => p.$size}px;
1388
+ }
1389
+ `;
1390
+ var PinDragSlot = import_styled_components6.default.div`
1391
+ position: relative;
1392
+ width: ${(p) => p.$size}px;
1393
+ height: ${(p) => p.$size}px;
1394
+ flex-shrink: 0;
1395
+ cursor: grab;
1396
+
1397
+ .${ITEM_BASE_CLASS}:active & {
1398
+ cursor: grabbing;
1399
+ }
1400
+ `;
1401
+ var PinDragLayer = import_styled_components6.default.div`
1402
+ position: absolute;
1403
+ inset: 0;
1404
+ display: flex;
1405
+ align-items: center;
1406
+ justify-content: center;
1407
+ color: inherit;
1408
+ opacity: ${(p) => p.$hideOnHover ? 1 : 0};
1409
+ transition: opacity 0.15s ease-in-out;
1410
+
1411
+ .${ITEM_BASE_CLASS}:hover & {
1412
+ opacity: ${(p) => p.$hideOnHover ? 0 : 1};
1413
+ }
1414
+
1415
+ svg {
1416
+ width: 12px;
1417
+ height: 12px;
1418
+ }
1419
+ `;
1420
+ var PinButton = import_styled_components6.default.button`
1421
+ display: flex;
1422
+ width: ${(p) => p.$size}px;
1423
+ height: ${(p) => p.$size}px;
1424
+ flex-shrink: 0;
1425
+ margin-left: auto;
1426
+ align-items: center;
1427
+ justify-content: center;
1428
+ padding: 0;
1429
+ border: none;
1430
+ background: transparent;
1431
+ color: ${(p) => p.$isPinned ? p.$activeColor : p.$color};
1432
+ cursor: pointer;
1433
+ border-radius: 4px;
1434
+ opacity: 0;
1435
+ transition:
1436
+ opacity 0.15s ease-in-out,
1437
+ color 0.15s ease-in-out;
1438
+
1439
+ .${ITEM_BASE_CLASS}:hover & {
1440
+ opacity: 1;
1441
+ }
1442
+
1443
+ &:hover {
1444
+ color: ${(p) => p.$hoverColor};
1445
+ }
1446
+
1447
+ &:focus-visible {
1448
+ opacity: 1;
1449
+ outline: 1px solid currentColor;
1450
+ outline-offset: 1px;
1451
+ }
1452
+
1453
+ svg {
1454
+ width: 12px;
1455
+ height: 12px;
1384
1456
  }
1385
1457
  `;
1386
1458
  var LabelBox = import_styled_components6.default.span`
@@ -1432,6 +1504,8 @@ var SidebarMenuItem = ({
1432
1504
  dataId,
1433
1505
  isActive,
1434
1506
  isPinned,
1507
+ onPinToggle,
1508
+ dragHandle,
1435
1509
  showBadge,
1436
1510
  hasTooltip,
1437
1511
  beta,
@@ -1472,7 +1546,10 @@ var SidebarMenuItem = ({
1472
1546
  activeClassName: ITEM_ACTIVE_CLASS,
1473
1547
  dataId,
1474
1548
  children: [
1475
- icon && !isNested && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconBox, { $size: sizing.iconSize, $pin: isPinned, children: icon }),
1549
+ !isNested && (isPinned && dragHandle ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(PinDragSlot, { $size: sizing.iconSize, "data-drag-handle": "true", children: [
1550
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PinDragLayer, { $size: sizing.iconSize, $hideOnHover: true, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_xui_icons_base2.Pin, { size: 12, variant: "solid" }) }),
1551
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PinDragLayer, { $size: sizing.iconSize, $hideOnHover: false, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_xui_icons_base2.Drag, { size: 12, variant: "line" }) })
1552
+ ] }) : isPinned ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconBox, { $size: sizing.iconSize, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_xui_icons_base2.Pin, { size: 12, variant: "solid" }) }) : icon ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconBox, { $size: sizing.iconSize, children: icon }) : null),
1476
1553
  extra,
1477
1554
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(LabelBox, { children: [
1478
1555
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
@@ -1492,6 +1569,33 @@ var SidebarMenuItem = ({
1492
1569
  }
1493
1570
  )
1494
1571
  ] }),
1572
+ onPinToggle && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1573
+ import_xui_tooltip.Tooltip,
1574
+ {
1575
+ content: isPinned ? "Unpin" : "Pin",
1576
+ placement: "top",
1577
+ size: "sm",
1578
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1579
+ PinButton,
1580
+ {
1581
+ type: "button",
1582
+ "aria-label": isPinned ? "Unpin item" : "Pin item",
1583
+ "aria-pressed": Boolean(isPinned),
1584
+ $size: sizing.iconSize,
1585
+ $color: theme.colors.content.tertiary,
1586
+ $hoverColor: theme.colors.content.primary,
1587
+ $activeColor: theme.colors.content.primary,
1588
+ $isPinned: Boolean(isPinned),
1589
+ onClick: (e) => {
1590
+ e.preventDefault();
1591
+ e.stopPropagation();
1592
+ onPinToggle(e);
1593
+ },
1594
+ children: isPinned ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_xui_icons_base2.Remove, { size: 12, variant: "line" }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_xui_icons_base2.Pin, { size: 12, variant: "solid" })
1595
+ }
1596
+ )
1597
+ }
1598
+ ),
1495
1599
  beta && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1496
1600
  BetaTag,
1497
1601
  {
@@ -1565,7 +1669,7 @@ var ExpandRegion = import_styled_components7.default.div`
1565
1669
  overflow: hidden;
1566
1670
  }
1567
1671
 
1568
- & .${ITEM_BASE_CLASS} {
1672
+ && .${ITEM_BASE_CLASS} {
1569
1673
  position: relative;
1570
1674
  padding: 0 ${(p) => p.$padding}px 0 ${(p) => p.$nestedPaddingLeft}px;
1571
1675
  font-weight: 400;
@@ -1582,7 +1686,7 @@ var ExpandRegion = import_styled_components7.default.div`
1582
1686
  }
1583
1687
  }
1584
1688
 
1585
- & .${ITEM_ACTIVE_CLASS} {
1689
+ && .${ITEM_ACTIVE_CLASS} {
1586
1690
  font-weight: 500;
1587
1691
  color: ${(p) => p.$colorActive};
1588
1692
  background-color: ${(p) => p.$activeBg};
@@ -1805,6 +1909,92 @@ var SidebarChatButton = ({
1805
1909
  }
1806
1910
  );
1807
1911
  };
1912
+
1913
+ // src/SidebarPinnedList.tsx
1914
+ var import_react6 = require("react");
1915
+ var import_xui_icons_base5 = require("@xsolla/xui-icons-base");
1916
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1917
+ var SidebarPinnedList = ({
1918
+ items,
1919
+ onUnpin,
1920
+ onReorder,
1921
+ spacers = true
1922
+ }) => {
1923
+ const [dragKey, setDragKey] = (0, import_react6.useState)(null);
1924
+ const canReorder = Boolean(onReorder) && items.length > 1;
1925
+ if (items.length === 0) return null;
1926
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
1927
+ spacers && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: { height: 8 } }),
1928
+ items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1929
+ "div",
1930
+ {
1931
+ "data-pinned-key": item.key,
1932
+ onPointerDown: (e) => {
1933
+ if (!canReorder) return;
1934
+ const t = e.target;
1935
+ if (!t.closest('[data-drag-handle="true"]')) return;
1936
+ e.preventDefault();
1937
+ setDragKey(item.key);
1938
+ e.currentTarget.setPointerCapture(e.pointerId);
1939
+ },
1940
+ onPointerMove: (e) => {
1941
+ if (dragKey !== item.key) return;
1942
+ const elt = document.elementFromPoint(
1943
+ e.clientX,
1944
+ e.clientY
1945
+ );
1946
+ const row = elt?.closest("[data-pinned-key]");
1947
+ const overKey = row?.getAttribute("data-pinned-key");
1948
+ if (overKey && overKey !== dragKey) {
1949
+ onReorder?.(dragKey, overKey);
1950
+ }
1951
+ },
1952
+ onPointerUp: (e) => {
1953
+ if (dragKey === item.key) {
1954
+ e.currentTarget.releasePointerCapture(
1955
+ e.pointerId
1956
+ );
1957
+ setDragKey(null);
1958
+ }
1959
+ },
1960
+ onPointerCancel: () => {
1961
+ if (dragKey === item.key) setDragKey(null);
1962
+ },
1963
+ style: {
1964
+ opacity: dragKey === item.key ? 0.4 : 1,
1965
+ transition: "opacity 0.15s ease-in-out",
1966
+ touchAction: canReorder ? "none" : void 0
1967
+ },
1968
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1969
+ SidebarMenuItem,
1970
+ {
1971
+ to: item.to,
1972
+ label: item.label,
1973
+ exact: item.exact,
1974
+ isPinned: true,
1975
+ dragHandle: canReorder,
1976
+ onPinToggle: () => onUnpin(item.key)
1977
+ }
1978
+ )
1979
+ },
1980
+ item.key
1981
+ )),
1982
+ spacers && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: { height: 8 } })
1983
+ ] });
1984
+ };
1985
+ var getPinnedCollapsedItem = (items, options = {}) => {
1986
+ if (items.length === 0) return null;
1987
+ return {
1988
+ label: options.label ?? "Pinned",
1989
+ icon: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_xui_icons_base5.Pin, { size: 18, variant: "solid" }),
1990
+ dataId: options.dataId ?? "sidebar-collapsed-pinned",
1991
+ children: items.map((i) => ({
1992
+ to: i.to,
1993
+ label: i.label,
1994
+ exact: i.exact
1995
+ }))
1996
+ };
1997
+ };
1808
1998
  // Annotate the CommonJS export names for ESM import in node:
1809
1999
  0 && (module.exports = {
1810
2000
  Sidebar,
@@ -1817,8 +2007,10 @@ var SidebarChatButton = ({
1817
2007
  SidebarMenuCollapsible,
1818
2008
  SidebarMenuItem,
1819
2009
  SidebarMenuSub,
2010
+ SidebarPinnedList,
1820
2011
  SidebarProvider,
1821
2012
  SidebarTrigger,
2013
+ getPinnedCollapsedItem,
1822
2014
  useSidebar
1823
2015
  });
1824
2016
  //# sourceMappingURL=index.js.map