@borisj74/bv-ds 0.1.4 → 0.1.6

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
@@ -2,6 +2,7 @@
2
2
 
3
3
  var React28 = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
+ var bvDsIcons = require('@borisj74/bv-ds-icons');
5
6
 
6
7
  function _interopNamespace(e) {
7
8
  if (e && e.__esModule) return e;
@@ -3843,7 +3844,7 @@ var require_factoryWithTypeCheckers = __commonJS({
3843
3844
  function emptyFunctionThatReturnsNull() {
3844
3845
  return null;
3845
3846
  }
3846
- module.exports = function(isValidElement12, throwOnDirectAccess) {
3847
+ module.exports = function(isValidElement13, throwOnDirectAccess) {
3847
3848
  var ITERATOR_SYMBOL = typeof Symbol === "function" && Symbol.iterator;
3848
3849
  var FAUX_ITERATOR_SYMBOL = "@@iterator";
3849
3850
  function getIteratorFn(maybeIterable) {
@@ -3971,7 +3972,7 @@ var require_factoryWithTypeCheckers = __commonJS({
3971
3972
  function createElementTypeChecker() {
3972
3973
  function validate(props, propName, componentName, location, propFullName) {
3973
3974
  var propValue = props[propName];
3974
- if (!isValidElement12(propValue)) {
3975
+ if (!isValidElement13(propValue)) {
3975
3976
  var propType = getPropType(propValue);
3976
3977
  return new PropTypeError("Invalid " + location + " `" + propFullName + "` of type " + ("`" + propType + "` supplied to `" + componentName + "`, expected a single ReactElement."));
3977
3978
  }
@@ -4159,7 +4160,7 @@ var require_factoryWithTypeCheckers = __commonJS({
4159
4160
  if (Array.isArray(propValue)) {
4160
4161
  return propValue.every(isNode);
4161
4162
  }
4162
- if (propValue === null || isValidElement12(propValue)) {
4163
+ if (propValue === null || isValidElement13(propValue)) {
4163
4164
  return true;
4164
4165
  }
4165
4166
  var iteratorFn = getIteratorFn(propValue);
@@ -31956,17 +31957,20 @@ function MultiSelect({
31956
31957
  hint && /* @__PURE__ */ jsxRuntime.jsx("span", { className: clsx_default("text-sm", invalid ? "text-text-error-primary" : "text-text-tertiary"), children: hint })
31957
31958
  ] });
31958
31959
  }
31959
- var ChevronSelector = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 16 16", fill: "none", className: "size-4", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 6l3-3 3 3M5 10l3 3 3-3", stroke: "currentColor", strokeWidth: "1.33", strokeLinecap: "round", strokeLinejoin: "round" }) });
31960
- var LogOut = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 16 16", fill: "none", className: "size-4", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(
31961
- "path",
31962
- {
31963
- d: "M10.67 11.33 14 8m0 0-3.33-3.33M14 8H6M6 2H4.67c-.93 0-1.4 0-1.76.18-.31.16-.57.42-.73.73C2 3.27 2 3.73 2 4.67v6.66c0 .94 0 1.4.18 1.76.16.31.42.57.73.73.36.18.83.18 1.76.18H6",
31964
- stroke: "currentColor",
31965
- strokeWidth: "1.33",
31966
- strokeLinecap: "round",
31967
- strokeLinejoin: "round"
31968
- }
31969
- ) });
31960
+ function IconBox({ size = 20, className, children }) {
31961
+ const icon = React28.isValidElement(children) ? React28.cloneElement(children, {
31962
+ viewBox: children.props.viewBox ?? "0 0 24 24",
31963
+ className: clsx_default("w-full h-full", children.props.className)
31964
+ }) : children;
31965
+ return /* @__PURE__ */ jsxRuntime.jsx(
31966
+ "span",
31967
+ {
31968
+ className: clsx_default("relative flex shrink-0 items-center justify-center", className),
31969
+ style: { width: size, height: size },
31970
+ children: icon
31971
+ }
31972
+ );
31973
+ }
31970
31974
  var LayeredAvatar = ({ src, online }) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative inline-flex size-10 shrink-0 rounded-full border-[0.75px] border-border-secondary-alt bg-bg-primary p-[1px] shadow-xs", children: [
31971
31975
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-full overflow-hidden rounded-full border-[0.5px] border-[rgba(0,0,0,0.16)]", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt: "", className: "size-full rounded-full object-cover" }) }),
31972
31976
  online !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
@@ -32001,7 +32005,7 @@ function NavAccountCard({
32001
32005
  className,
32002
32006
  ...rest
32003
32007
  }) {
32004
- const widthClass = breakpoint === "mobile" ? "w-[256px]" : "w-[280px]";
32008
+ const widthClass = breakpoint === "mobile" ? "w-full max-w-[256px]" : "w-full max-w-[280px]";
32005
32009
  const avatarNode = avatar ?? (src !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(LayeredAvatar, { src, online }) : void 0);
32006
32010
  if (variant === "simple") {
32007
32011
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -32022,7 +32026,7 @@ function NavAccountCard({
32022
32026
  onClick: onSignOut,
32023
32027
  "aria-label": "Sign out",
32024
32028
  className: "absolute right-0 top-[15px] flex items-center justify-center rounded-sm p-sm text-fg-quaternary transition-colors hover:text-fg-quaternary-hover",
32025
- children: /* @__PURE__ */ jsxRuntime.jsx(LogOut, {})
32029
+ children: /* @__PURE__ */ jsxRuntime.jsx(IconBox, { size: 16, children: /* @__PURE__ */ jsxRuntime.jsx(bvDsIcons.LogOut01, {}) })
32026
32030
  }
32027
32031
  )
32028
32032
  ]
@@ -32045,7 +32049,7 @@ function NavAccountCard({
32045
32049
  "absolute right-[7px] top-[7px] flex items-center justify-center rounded-sm p-sm text-fg-quaternary",
32046
32050
  open && "bg-bg-primary-hover"
32047
32051
  ),
32048
- children: /* @__PURE__ */ jsxRuntime.jsx(ChevronSelector, {})
32052
+ children: /* @__PURE__ */ jsxRuntime.jsx(IconBox, { size: 16, children: /* @__PURE__ */ jsxRuntime.jsx(bvDsIcons.ChevronSelectorVertical, {}) })
32049
32053
  }
32050
32054
  )
32051
32055
  ]
@@ -32186,7 +32190,6 @@ function NavFeaturedCard({
32186
32190
  }
32187
32191
  );
32188
32192
  }
32189
- var ChevronDown2 = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 16 16", fill: "none", className: "size-4", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 6l4 4 4-4", stroke: "currentColor", strokeWidth: "1.33", strokeLinecap: "round", strokeLinejoin: "round" }) });
32190
32193
  function NavItemBase({
32191
32194
  current = false,
32192
32195
  icon,
@@ -32203,7 +32206,7 @@ function NavItemBase({
32203
32206
  {
32204
32207
  type,
32205
32208
  className: clsx_default(
32206
- "flex w-[272px] items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2",
32209
+ "flex w-full items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2",
32207
32210
  className
32208
32211
  ),
32209
32212
  ...rest,
@@ -32217,18 +32220,17 @@ function NavItemBase({
32217
32220
  children: [
32218
32221
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-md", children: [
32219
32222
  dot && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-2 shrink-0 rounded-full bg-fg-success-secondary" }),
32220
- icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-5 shrink-0 items-center justify-center text-fg-quaternary [&>svg]:size-5", children: icon }),
32223
+ icon && /* @__PURE__ */ jsxRuntime.jsx(IconBox, { size: 20, className: "text-fg-quaternary", children: icon }),
32221
32224
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-left text-sm font-semibold text-text-secondary", children: label })
32222
32225
  ] }),
32223
32226
  badge != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 rounded-full border border-utility-neutral-200 bg-utility-neutral-50 px-md py-xxs text-xs font-medium text-utility-neutral-700", children: badge }),
32224
- trailingChevron && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-fg-quaternary", children: /* @__PURE__ */ jsxRuntime.jsx(ChevronDown2, {}) })
32227
+ trailingChevron && /* @__PURE__ */ jsxRuntime.jsx(IconBox, { size: 16, className: "text-fg-quaternary", children: /* @__PURE__ */ jsxRuntime.jsx(bvDsIcons.ChevronDown, {}) })
32225
32228
  ]
32226
32229
  }
32227
32230
  )
32228
32231
  }
32229
32232
  );
32230
32233
  }
32231
- var Chevron5 = ({ up }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 16 16", fill: "none", className: "size-4", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: up ? "M4 10l4-4 4 4" : "M4 6l4 4 4-4", stroke: "currentColor", strokeWidth: "1.33", strokeLinecap: "round", strokeLinejoin: "round" }) });
32232
32234
  function NavItemDropdownBase({
32233
32235
  current = false,
32234
32236
  open = false,
@@ -32240,13 +32242,13 @@ function NavItemDropdownBase({
32240
32242
  type = "button",
32241
32243
  ...rest
32242
32244
  }) {
32243
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: clsx_default("flex w-[272px] flex-col", className), children: [
32245
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: clsx_default("flex w-full flex-col", className), children: [
32244
32246
  /* @__PURE__ */ jsxRuntime.jsx(
32245
32247
  "button",
32246
32248
  {
32247
32249
  type,
32248
32250
  onClick: onToggle,
32249
- className: "flex items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2",
32251
+ className: "flex w-full items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2",
32250
32252
  ...rest,
32251
32253
  children: /* @__PURE__ */ jsxRuntime.jsxs(
32252
32254
  "div",
@@ -32257,10 +32259,10 @@ function NavItemDropdownBase({
32257
32259
  ),
32258
32260
  children: [
32259
32261
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-md", children: [
32260
- icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-5 shrink-0 items-center justify-center text-fg-quaternary [&>svg]:size-5", children: icon }),
32262
+ icon && /* @__PURE__ */ jsxRuntime.jsx(IconBox, { size: 20, className: "text-fg-quaternary", children: icon }),
32261
32263
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-left text-sm font-semibold text-text-secondary", children: label })
32262
32264
  ] }),
32263
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-fg-quaternary", children: /* @__PURE__ */ jsxRuntime.jsx(Chevron5, { up: open }) })
32265
+ /* @__PURE__ */ jsxRuntime.jsx(IconBox, { size: 16, className: "text-fg-quaternary", children: /* @__PURE__ */ jsxRuntime.jsx(bvDsIcons.ChevronDown, { className: clsx_default("transition-transform", open && "rotate-180") }) })
32264
32266
  ]
32265
32267
  }
32266
32268
  )
@@ -32269,8 +32271,6 @@ function NavItemDropdownBase({
32269
32271
  open && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col pb-xs", children })
32270
32272
  ] });
32271
32273
  }
32272
- var Menu = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 20 20", fill: "none", className: "size-5", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2.5 10h15M2.5 5h15M2.5 15h15", stroke: "currentColor", strokeWidth: "1.67", strokeLinecap: "round", strokeLinejoin: "round" }) });
32273
- var XClose4 = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 20 20", fill: "none", className: "size-5", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 5 5 15M5 5l10 10", stroke: "currentColor", strokeWidth: "1.67", strokeLinecap: "round", strokeLinejoin: "round" }) });
32274
32274
  function NavMenuButton({
32275
32275
  opened = false,
32276
32276
  className,
@@ -32289,7 +32289,7 @@ function NavMenuButton({
32289
32289
  className
32290
32290
  ),
32291
32291
  ...rest,
32292
- children: opened ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-70", children: /* @__PURE__ */ jsxRuntime.jsx(XClose4, {}) }) : /* @__PURE__ */ jsxRuntime.jsx(Menu, {})
32292
+ children: opened ? /* @__PURE__ */ jsxRuntime.jsx(IconBox, { size: 20, className: "opacity-70", children: /* @__PURE__ */ jsxRuntime.jsx(bvDsIcons.XClose, {}) }) : /* @__PURE__ */ jsxRuntime.jsx(IconBox, { size: 20, children: /* @__PURE__ */ jsxRuntime.jsx(bvDsIcons.Menu02, {}) })
32293
32293
  }
32294
32294
  );
32295
32295
  }
@@ -32377,7 +32377,7 @@ function NumberInput({
32377
32377
  const inc = () => set2(value + step);
32378
32378
  const Minus = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 20 20", fill: "none", className: "size-5", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4.167 10h11.666", stroke: "currentColor", strokeWidth: "1.67", strokeLinecap: "round" }) });
32379
32379
  const Plus = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 20 20", fill: "none", className: "size-5", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 4.167v11.666M4.167 10h11.666", stroke: "currentColor", strokeWidth: "1.67", strokeLinecap: "round" }) });
32380
- const Chevron6 = ({ up }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 12 12", fill: "none", className: clsx_default("size-3", up && "rotate-180"), "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 4.5 6 7.5 9 4.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" }) });
32380
+ const Chevron5 = ({ up }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 12 12", fill: "none", className: clsx_default("size-3", up && "rotate-180"), "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 4.5 6 7.5 9 4.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" }) });
32381
32381
  const numberEl = /* @__PURE__ */ jsxRuntime.jsx(
32382
32382
  "input",
32383
32383
  {
@@ -32403,9 +32403,9 @@ function NumberInput({
32403
32403
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: clsx_default(boxClasses(destructive), "gap-0 overflow-hidden p-0"), children: [
32404
32404
  numberEl,
32405
32405
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col self-stretch border-l border-border-primary", children: [
32406
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: inc, disabled, "aria-label": "Increase", className: "flex flex-1 items-center justify-center px-md text-fg-quaternary hover:bg-bg-primary-hover disabled:opacity-60", children: /* @__PURE__ */ jsxRuntime.jsx(Chevron6, { up: true }) }),
32406
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: inc, disabled, "aria-label": "Increase", className: "flex flex-1 items-center justify-center px-md text-fg-quaternary hover:bg-bg-primary-hover disabled:opacity-60", children: /* @__PURE__ */ jsxRuntime.jsx(Chevron5, { up: true }) }),
32407
32407
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-px w-full bg-border-primary" }),
32408
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: dec, disabled, "aria-label": "Decrease", className: "flex flex-1 items-center justify-center px-md text-fg-quaternary hover:bg-bg-primary-hover disabled:opacity-60", children: /* @__PURE__ */ jsxRuntime.jsx(Chevron6, { up: false }) })
32408
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: dec, disabled, "aria-label": "Decrease", className: "flex flex-1 items-center justify-center px-md text-fg-quaternary hover:bg-bg-primary-hover disabled:opacity-60", children: /* @__PURE__ */ jsxRuntime.jsx(Chevron5, { up: false }) })
32409
32409
  ] })
32410
32410
  ] });
32411
32411
  return /* @__PURE__ */ jsxRuntime.jsx(FieldWrapper, { label, required, hint, destructive, className, children: box });
@@ -33138,17 +33138,20 @@ function SidebarNavigation({
33138
33138
  "nav",
33139
33139
  {
33140
33140
  className: clsx_default(
33141
+ // Padding is applied per-section (Figma node 1161:8593): header 20px,
33142
+ // nav items 16px, footer 16px/20px — not a uniform rail inset, so the
33143
+ // full-width nav items align to their own 16px gutter without clipping.
33141
33144
  "flex h-full flex-col gap-2xl border-r border-border-secondary bg-bg-primary",
33142
- slim ? "items-center px-md py-xl" : "px-lg py-xl",
33145
+ slim ? "items-center px-md py-xl" : "py-xl",
33143
33146
  WIDTH[type],
33144
33147
  className
33145
33148
  ),
33146
33149
  ...rest,
33147
33150
  children: [
33148
- logo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx_default("shrink-0", slim ? "" : "px-md"), children: logo }),
33149
- header && !slim && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: header }),
33150
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-0 flex-1 flex-col gap-xxs overflow-y-auto", children }),
33151
- footer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex shrink-0 flex-col gap-lg", children: footer })
33151
+ logo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx_default("shrink-0", slim ? "" : "px-2xl"), children: logo }),
33152
+ header && !slim && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 px-2xl", children: header }),
33153
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx_default("flex min-h-0 flex-1 flex-col gap-xxs overflow-y-auto", !slim && "px-xl"), children }),
33154
+ footer && /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx_default("flex shrink-0 flex-col gap-lg", !slim && "px-xl"), children: footer })
33152
33155
  ]
33153
33156
  }
33154
33157
  );
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as React28 from 'react';
2
2
  import React28__default, { forwardRef, isValidElement, cloneElement, Children, useContext, useMemo, createContext, Fragment, useState, PureComponent, createElement, Component, useRef, useEffect, useCallback, useId, useImperativeHandle } from 'react';
3
3
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
4
+ import { LogOut01, ChevronSelectorVertical, ChevronDown as ChevronDown$1, XClose as XClose$1, Menu02 } from '@borisj74/bv-ds-icons';
4
5
 
5
6
  var __create = Object.create;
6
7
  var __defProp = Object.defineProperty;
@@ -3822,7 +3823,7 @@ var require_factoryWithTypeCheckers = __commonJS({
3822
3823
  function emptyFunctionThatReturnsNull() {
3823
3824
  return null;
3824
3825
  }
3825
- module.exports = function(isValidElement12, throwOnDirectAccess) {
3826
+ module.exports = function(isValidElement13, throwOnDirectAccess) {
3826
3827
  var ITERATOR_SYMBOL = typeof Symbol === "function" && Symbol.iterator;
3827
3828
  var FAUX_ITERATOR_SYMBOL = "@@iterator";
3828
3829
  function getIteratorFn(maybeIterable) {
@@ -3950,7 +3951,7 @@ var require_factoryWithTypeCheckers = __commonJS({
3950
3951
  function createElementTypeChecker() {
3951
3952
  function validate(props, propName, componentName, location, propFullName) {
3952
3953
  var propValue = props[propName];
3953
- if (!isValidElement12(propValue)) {
3954
+ if (!isValidElement13(propValue)) {
3954
3955
  var propType = getPropType(propValue);
3955
3956
  return new PropTypeError("Invalid " + location + " `" + propFullName + "` of type " + ("`" + propType + "` supplied to `" + componentName + "`, expected a single ReactElement."));
3956
3957
  }
@@ -4138,7 +4139,7 @@ var require_factoryWithTypeCheckers = __commonJS({
4138
4139
  if (Array.isArray(propValue)) {
4139
4140
  return propValue.every(isNode);
4140
4141
  }
4141
- if (propValue === null || isValidElement12(propValue)) {
4142
+ if (propValue === null || isValidElement13(propValue)) {
4142
4143
  return true;
4143
4144
  }
4144
4145
  var iteratorFn = getIteratorFn(propValue);
@@ -31935,17 +31936,20 @@ function MultiSelect({
31935
31936
  hint && /* @__PURE__ */ jsx("span", { className: clsx_default("text-sm", invalid ? "text-text-error-primary" : "text-text-tertiary"), children: hint })
31936
31937
  ] });
31937
31938
  }
31938
- var ChevronSelector = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", className: "size-4", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M5 6l3-3 3 3M5 10l3 3 3-3", stroke: "currentColor", strokeWidth: "1.33", strokeLinecap: "round", strokeLinejoin: "round" }) });
31939
- var LogOut = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", className: "size-4", "aria-hidden": true, children: /* @__PURE__ */ jsx(
31940
- "path",
31941
- {
31942
- d: "M10.67 11.33 14 8m0 0-3.33-3.33M14 8H6M6 2H4.67c-.93 0-1.4 0-1.76.18-.31.16-.57.42-.73.73C2 3.27 2 3.73 2 4.67v6.66c0 .94 0 1.4.18 1.76.16.31.42.57.73.73.36.18.83.18 1.76.18H6",
31943
- stroke: "currentColor",
31944
- strokeWidth: "1.33",
31945
- strokeLinecap: "round",
31946
- strokeLinejoin: "round"
31947
- }
31948
- ) });
31939
+ function IconBox({ size = 20, className, children }) {
31940
+ const icon = isValidElement(children) ? cloneElement(children, {
31941
+ viewBox: children.props.viewBox ?? "0 0 24 24",
31942
+ className: clsx_default("w-full h-full", children.props.className)
31943
+ }) : children;
31944
+ return /* @__PURE__ */ jsx(
31945
+ "span",
31946
+ {
31947
+ className: clsx_default("relative flex shrink-0 items-center justify-center", className),
31948
+ style: { width: size, height: size },
31949
+ children: icon
31950
+ }
31951
+ );
31952
+ }
31949
31953
  var LayeredAvatar = ({ src, online }) => /* @__PURE__ */ jsxs("span", { className: "relative inline-flex size-10 shrink-0 rounded-full border-[0.75px] border-border-secondary-alt bg-bg-primary p-[1px] shadow-xs", children: [
31950
31954
  /* @__PURE__ */ jsx("span", { className: "flex size-full overflow-hidden rounded-full border-[0.5px] border-[rgba(0,0,0,0.16)]", children: /* @__PURE__ */ jsx("img", { src, alt: "", className: "size-full rounded-full object-cover" }) }),
31951
31955
  online !== void 0 && /* @__PURE__ */ jsx(
@@ -31980,7 +31984,7 @@ function NavAccountCard({
31980
31984
  className,
31981
31985
  ...rest
31982
31986
  }) {
31983
- const widthClass = breakpoint === "mobile" ? "w-[256px]" : "w-[280px]";
31987
+ const widthClass = breakpoint === "mobile" ? "w-full max-w-[256px]" : "w-full max-w-[280px]";
31984
31988
  const avatarNode = avatar ?? (src !== void 0 ? /* @__PURE__ */ jsx(LayeredAvatar, { src, online }) : void 0);
31985
31989
  if (variant === "simple") {
31986
31990
  return /* @__PURE__ */ jsxs(
@@ -32001,7 +32005,7 @@ function NavAccountCard({
32001
32005
  onClick: onSignOut,
32002
32006
  "aria-label": "Sign out",
32003
32007
  className: "absolute right-0 top-[15px] flex items-center justify-center rounded-sm p-sm text-fg-quaternary transition-colors hover:text-fg-quaternary-hover",
32004
- children: /* @__PURE__ */ jsx(LogOut, {})
32008
+ children: /* @__PURE__ */ jsx(IconBox, { size: 16, children: /* @__PURE__ */ jsx(LogOut01, {}) })
32005
32009
  }
32006
32010
  )
32007
32011
  ]
@@ -32024,7 +32028,7 @@ function NavAccountCard({
32024
32028
  "absolute right-[7px] top-[7px] flex items-center justify-center rounded-sm p-sm text-fg-quaternary",
32025
32029
  open && "bg-bg-primary-hover"
32026
32030
  ),
32027
- children: /* @__PURE__ */ jsx(ChevronSelector, {})
32031
+ children: /* @__PURE__ */ jsx(IconBox, { size: 16, children: /* @__PURE__ */ jsx(ChevronSelectorVertical, {}) })
32028
32032
  }
32029
32033
  )
32030
32034
  ]
@@ -32165,7 +32169,6 @@ function NavFeaturedCard({
32165
32169
  }
32166
32170
  );
32167
32171
  }
32168
- var ChevronDown2 = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", className: "size-4", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M4 6l4 4 4-4", stroke: "currentColor", strokeWidth: "1.33", strokeLinecap: "round", strokeLinejoin: "round" }) });
32169
32172
  function NavItemBase({
32170
32173
  current = false,
32171
32174
  icon,
@@ -32182,7 +32185,7 @@ function NavItemBase({
32182
32185
  {
32183
32186
  type,
32184
32187
  className: clsx_default(
32185
- "flex w-[272px] items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2",
32188
+ "flex w-full items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2",
32186
32189
  className
32187
32190
  ),
32188
32191
  ...rest,
@@ -32196,18 +32199,17 @@ function NavItemBase({
32196
32199
  children: [
32197
32200
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-md", children: [
32198
32201
  dot && /* @__PURE__ */ jsx("span", { className: "size-2 shrink-0 rounded-full bg-fg-success-secondary" }),
32199
- icon && /* @__PURE__ */ jsx("span", { className: "flex size-5 shrink-0 items-center justify-center text-fg-quaternary [&>svg]:size-5", children: icon }),
32202
+ icon && /* @__PURE__ */ jsx(IconBox, { size: 20, className: "text-fg-quaternary", children: icon }),
32200
32203
  /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left text-sm font-semibold text-text-secondary", children: label })
32201
32204
  ] }),
32202
32205
  badge != null && /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded-full border border-utility-neutral-200 bg-utility-neutral-50 px-md py-xxs text-xs font-medium text-utility-neutral-700", children: badge }),
32203
- trailingChevron && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-fg-quaternary", children: /* @__PURE__ */ jsx(ChevronDown2, {}) })
32206
+ trailingChevron && /* @__PURE__ */ jsx(IconBox, { size: 16, className: "text-fg-quaternary", children: /* @__PURE__ */ jsx(ChevronDown$1, {}) })
32204
32207
  ]
32205
32208
  }
32206
32209
  )
32207
32210
  }
32208
32211
  );
32209
32212
  }
32210
- var Chevron5 = ({ up }) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", className: "size-4", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: up ? "M4 10l4-4 4 4" : "M4 6l4 4 4-4", stroke: "currentColor", strokeWidth: "1.33", strokeLinecap: "round", strokeLinejoin: "round" }) });
32211
32213
  function NavItemDropdownBase({
32212
32214
  current = false,
32213
32215
  open = false,
@@ -32219,13 +32221,13 @@ function NavItemDropdownBase({
32219
32221
  type = "button",
32220
32222
  ...rest
32221
32223
  }) {
32222
- return /* @__PURE__ */ jsxs("div", { className: clsx_default("flex w-[272px] flex-col", className), children: [
32224
+ return /* @__PURE__ */ jsxs("div", { className: clsx_default("flex w-full flex-col", className), children: [
32223
32225
  /* @__PURE__ */ jsx(
32224
32226
  "button",
32225
32227
  {
32226
32228
  type,
32227
32229
  onClick: onToggle,
32228
- className: "flex items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2",
32230
+ className: "flex w-full items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2",
32229
32231
  ...rest,
32230
32232
  children: /* @__PURE__ */ jsxs(
32231
32233
  "div",
@@ -32236,10 +32238,10 @@ function NavItemDropdownBase({
32236
32238
  ),
32237
32239
  children: [
32238
32240
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-md", children: [
32239
- icon && /* @__PURE__ */ jsx("span", { className: "flex size-5 shrink-0 items-center justify-center text-fg-quaternary [&>svg]:size-5", children: icon }),
32241
+ icon && /* @__PURE__ */ jsx(IconBox, { size: 20, className: "text-fg-quaternary", children: icon }),
32240
32242
  /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left text-sm font-semibold text-text-secondary", children: label })
32241
32243
  ] }),
32242
- /* @__PURE__ */ jsx("span", { className: "shrink-0 text-fg-quaternary", children: /* @__PURE__ */ jsx(Chevron5, { up: open }) })
32244
+ /* @__PURE__ */ jsx(IconBox, { size: 16, className: "text-fg-quaternary", children: /* @__PURE__ */ jsx(ChevronDown$1, { className: clsx_default("transition-transform", open && "rotate-180") }) })
32243
32245
  ]
32244
32246
  }
32245
32247
  )
@@ -32248,8 +32250,6 @@ function NavItemDropdownBase({
32248
32250
  open && /* @__PURE__ */ jsx("div", { className: "flex flex-col pb-xs", children })
32249
32251
  ] });
32250
32252
  }
32251
- var Menu = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", fill: "none", className: "size-5", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2.5 10h15M2.5 5h15M2.5 15h15", stroke: "currentColor", strokeWidth: "1.67", strokeLinecap: "round", strokeLinejoin: "round" }) });
32252
- var XClose4 = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", fill: "none", className: "size-5", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M15 5 5 15M5 5l10 10", stroke: "currentColor", strokeWidth: "1.67", strokeLinecap: "round", strokeLinejoin: "round" }) });
32253
32253
  function NavMenuButton({
32254
32254
  opened = false,
32255
32255
  className,
@@ -32268,7 +32268,7 @@ function NavMenuButton({
32268
32268
  className
32269
32269
  ),
32270
32270
  ...rest,
32271
- children: opened ? /* @__PURE__ */ jsx("span", { className: "opacity-70", children: /* @__PURE__ */ jsx(XClose4, {}) }) : /* @__PURE__ */ jsx(Menu, {})
32271
+ children: opened ? /* @__PURE__ */ jsx(IconBox, { size: 20, className: "opacity-70", children: /* @__PURE__ */ jsx(XClose$1, {}) }) : /* @__PURE__ */ jsx(IconBox, { size: 20, children: /* @__PURE__ */ jsx(Menu02, {}) })
32272
32272
  }
32273
32273
  );
32274
32274
  }
@@ -32356,7 +32356,7 @@ function NumberInput({
32356
32356
  const inc = () => set2(value + step);
32357
32357
  const Minus = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", fill: "none", className: "size-5", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M4.167 10h11.666", stroke: "currentColor", strokeWidth: "1.67", strokeLinecap: "round" }) });
32358
32358
  const Plus = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", fill: "none", className: "size-5", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M10 4.167v11.666M4.167 10h11.666", stroke: "currentColor", strokeWidth: "1.67", strokeLinecap: "round" }) });
32359
- const Chevron6 = ({ up }) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", fill: "none", className: clsx_default("size-3", up && "rotate-180"), "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M3 4.5 6 7.5 9 4.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" }) });
32359
+ const Chevron5 = ({ up }) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", fill: "none", className: clsx_default("size-3", up && "rotate-180"), "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M3 4.5 6 7.5 9 4.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" }) });
32360
32360
  const numberEl = /* @__PURE__ */ jsx(
32361
32361
  "input",
32362
32362
  {
@@ -32382,9 +32382,9 @@ function NumberInput({
32382
32382
  ] }) : /* @__PURE__ */ jsxs("div", { className: clsx_default(boxClasses(destructive), "gap-0 overflow-hidden p-0"), children: [
32383
32383
  numberEl,
32384
32384
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col self-stretch border-l border-border-primary", children: [
32385
- /* @__PURE__ */ jsx("button", { type: "button", onClick: inc, disabled, "aria-label": "Increase", className: "flex flex-1 items-center justify-center px-md text-fg-quaternary hover:bg-bg-primary-hover disabled:opacity-60", children: /* @__PURE__ */ jsx(Chevron6, { up: true }) }),
32385
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: inc, disabled, "aria-label": "Increase", className: "flex flex-1 items-center justify-center px-md text-fg-quaternary hover:bg-bg-primary-hover disabled:opacity-60", children: /* @__PURE__ */ jsx(Chevron5, { up: true }) }),
32386
32386
  /* @__PURE__ */ jsx("span", { className: "h-px w-full bg-border-primary" }),
32387
- /* @__PURE__ */ jsx("button", { type: "button", onClick: dec, disabled, "aria-label": "Decrease", className: "flex flex-1 items-center justify-center px-md text-fg-quaternary hover:bg-bg-primary-hover disabled:opacity-60", children: /* @__PURE__ */ jsx(Chevron6, { up: false }) })
32387
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: dec, disabled, "aria-label": "Decrease", className: "flex flex-1 items-center justify-center px-md text-fg-quaternary hover:bg-bg-primary-hover disabled:opacity-60", children: /* @__PURE__ */ jsx(Chevron5, { up: false }) })
32388
32388
  ] })
32389
32389
  ] });
32390
32390
  return /* @__PURE__ */ jsx(FieldWrapper, { label, required, hint, destructive, className, children: box });
@@ -33117,17 +33117,20 @@ function SidebarNavigation({
33117
33117
  "nav",
33118
33118
  {
33119
33119
  className: clsx_default(
33120
+ // Padding is applied per-section (Figma node 1161:8593): header 20px,
33121
+ // nav items 16px, footer 16px/20px — not a uniform rail inset, so the
33122
+ // full-width nav items align to their own 16px gutter without clipping.
33120
33123
  "flex h-full flex-col gap-2xl border-r border-border-secondary bg-bg-primary",
33121
- slim ? "items-center px-md py-xl" : "px-lg py-xl",
33124
+ slim ? "items-center px-md py-xl" : "py-xl",
33122
33125
  WIDTH[type],
33123
33126
  className
33124
33127
  ),
33125
33128
  ...rest,
33126
33129
  children: [
33127
- logo && /* @__PURE__ */ jsx("div", { className: clsx_default("shrink-0", slim ? "" : "px-md"), children: logo }),
33128
- header && !slim && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: header }),
33129
- /* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 flex-col gap-xxs overflow-y-auto", children }),
33130
- footer && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 flex-col gap-lg", children: footer })
33130
+ logo && /* @__PURE__ */ jsx("div", { className: clsx_default("shrink-0", slim ? "" : "px-2xl"), children: logo }),
33131
+ header && !slim && /* @__PURE__ */ jsx("div", { className: "shrink-0 px-2xl", children: header }),
33132
+ /* @__PURE__ */ jsx("div", { className: clsx_default("flex min-h-0 flex-1 flex-col gap-xxs overflow-y-auto", !slim && "px-xl"), children }),
33133
+ footer && /* @__PURE__ */ jsx("div", { className: clsx_default("flex shrink-0 flex-col gap-lg", !slim && "px-xl"), children: footer })
33131
33134
  ]
33132
33135
  }
33133
33136
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@borisj74/bv-ds",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "bv-ds — React component library synced from Figma (Untitled UI v8.0), built on Tailwind CSS",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -35,7 +35,8 @@
35
35
  },
36
36
  "peerDependencies": {
37
37
  "react": ">=18.0.0",
38
- "react-dom": ">=18.0.0"
38
+ "react-dom": ">=18.0.0",
39
+ "@borisj74/bv-ds-icons": ">=0.1.0"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@borisj74/bv-ds-icons": "^0.1.0",
@@ -1,5 +1,7 @@
1
1
  import { type HTMLAttributes, type ReactNode } from "react";
2
2
  import clsx from "clsx";
3
+ import { ChevronSelectorVertical, LogOut01 } from "@borisj74/bv-ds-icons";
4
+ import { IconBox } from "../../internal/iconBox";
3
5
 
4
6
  export type NavAccountCardVariant = "simple" | "card";
5
7
  export type NavAccountCardBreakpoint = "desktop" | "mobile";
@@ -31,24 +33,6 @@ export interface NavAccountCardProps
31
33
  menu?: ReactNode;
32
34
  }
33
35
 
34
- const ChevronSelector = () => (
35
- <svg viewBox="0 0 16 16" fill="none" className="size-4" aria-hidden>
36
- <path d="M5 6l3-3 3 3M5 10l3 3 3-3" stroke="currentColor" strokeWidth="1.33" strokeLinecap="round" strokeLinejoin="round" />
37
- </svg>
38
- );
39
-
40
- const LogOut = () => (
41
- <svg viewBox="0 0 16 16" fill="none" className="size-4" aria-hidden>
42
- <path
43
- d="M10.67 11.33 14 8m0 0-3.33-3.33M14 8H6M6 2H4.67c-.93 0-1.4 0-1.76.18-.31.16-.57.42-.73.73C2 3.27 2 3.73 2 4.67v6.66c0 .94 0 1.4.18 1.76.16.31.42.57.73.73.36.18.83.18 1.76.18H6"
44
- stroke="currentColor"
45
- strokeWidth="1.33"
46
- strokeLinecap="round"
47
- strokeLinejoin="round"
48
- />
49
- </svg>
50
- );
51
-
52
36
  /**
53
37
  * Layered avatar treatment from Figma (node 7891:87996): white outer ring +
54
38
  * inner hairline border + status dot. `border-[rgba(0,0,0,0.16)]` is a one-off
@@ -107,7 +91,9 @@ export function NavAccountCard({
107
91
  className,
108
92
  ...rest
109
93
  }: NavAccountCardProps) {
110
- const widthClass = breakpoint === "mobile" ? "w-[256px]" : "w-[280px]";
94
+ // Fill the container (e.g. a padded sidebar footer) but cap at the Figma
95
+ // standalone width so it doesn't stretch unbounded outside a sidebar.
96
+ const widthClass = breakpoint === "mobile" ? "w-full max-w-[256px]" : "w-full max-w-[280px]";
111
97
  const avatarNode = avatar ?? (src !== undefined ? <LayeredAvatar src={src} online={online} /> : undefined);
112
98
 
113
99
  if (variant === "simple") {
@@ -127,7 +113,9 @@ export function NavAccountCard({
127
113
  aria-label="Sign out"
128
114
  className="absolute right-0 top-[15px] flex items-center justify-center rounded-sm p-sm text-fg-quaternary transition-colors hover:text-fg-quaternary-hover"
129
115
  >
130
- <LogOut />
116
+ <IconBox size={16}>
117
+ <LogOut01 />
118
+ </IconBox>
131
119
  </button>
132
120
  </div>
133
121
  );
@@ -147,7 +135,9 @@ export function NavAccountCard({
147
135
  open && "bg-bg-primary-hover",
148
136
  )}
149
137
  >
150
- <ChevronSelector />
138
+ <IconBox size={16}>
139
+ <ChevronSelectorVertical />
140
+ </IconBox>
151
141
  </span>
152
142
  </button>
153
143
 
@@ -1,5 +1,7 @@
1
1
  import { type ButtonHTMLAttributes, type ReactNode } from "react";
2
2
  import clsx from "clsx";
3
+ import { ChevronDown } from "@borisj74/bv-ds-icons";
4
+ import { IconBox } from "../../internal/iconBox";
3
5
 
4
6
  export interface NavItemBaseProps
5
7
  extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
@@ -16,12 +18,6 @@ export interface NavItemBaseProps
16
18
  trailingChevron?: boolean;
17
19
  }
18
20
 
19
- const ChevronDown = () => (
20
- <svg viewBox="0 0 16 16" fill="none" className="size-4" aria-hidden>
21
- <path d="M4 6l4 4 4-4" stroke="currentColor" strokeWidth="1.33" strokeLinecap="round" strokeLinejoin="round" />
22
- </svg>
23
- );
24
-
25
21
  /**
26
22
  * Base sidebar nav link (272px). `current` selects it; optional leading `dot`,
27
23
  * leading `icon`, trailing count `badge`, and `trailingChevron`. Brand focus ring.
@@ -41,7 +37,7 @@ export function NavItemBase({
41
37
  <button
42
38
  type={type}
43
39
  className={clsx(
44
- "flex w-[272px] items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2",
40
+ "flex w-full items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2",
45
41
  className,
46
42
  )}
47
43
  {...rest}
@@ -55,9 +51,9 @@ export function NavItemBase({
55
51
  <div className="flex min-w-0 flex-1 items-center gap-md">
56
52
  {dot && <span className="size-2 shrink-0 rounded-full bg-fg-success-secondary" />}
57
53
  {icon && (
58
- <span className="flex size-5 shrink-0 items-center justify-center text-fg-quaternary [&>svg]:size-5">
54
+ <IconBox size={20} className="text-fg-quaternary">
59
55
  {icon}
60
- </span>
56
+ </IconBox>
61
57
  )}
62
58
  <span className="min-w-0 flex-1 truncate text-left text-sm font-semibold text-text-secondary">
63
59
  {label}
@@ -69,9 +65,9 @@ export function NavItemBase({
69
65
  </span>
70
66
  )}
71
67
  {trailingChevron && (
72
- <span className="shrink-0 text-fg-quaternary">
68
+ <IconBox size={16} className="text-fg-quaternary">
73
69
  <ChevronDown />
74
- </span>
70
+ </IconBox>
75
71
  )}
76
72
  </div>
77
73
  </button>
@@ -1,5 +1,7 @@
1
1
  import { type ButtonHTMLAttributes, type ReactNode } from "react";
2
2
  import clsx from "clsx";
3
+ import { ChevronDown } from "@borisj74/bv-ds-icons";
4
+ import { IconBox } from "../../internal/iconBox";
3
5
 
4
6
  export interface NavItemDropdownBaseProps
5
7
  extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
@@ -16,12 +18,6 @@ export interface NavItemDropdownBaseProps
16
18
  children?: ReactNode;
17
19
  }
18
20
 
19
- const Chevron = ({ up }: { up?: boolean }) => (
20
- <svg viewBox="0 0 16 16" fill="none" className="size-4" aria-hidden>
21
- <path d={up ? "M4 10l4-4 4 4" : "M4 6l4 4 4-4"} stroke="currentColor" strokeWidth="1.33" strokeLinecap="round" strokeLinejoin="round" />
22
- </svg>
23
- );
24
-
25
21
  /**
26
22
  * Expandable sidebar nav group (272px). The trigger mirrors `NavItemBase`; when
27
23
  * `open`, `children` (indented submenu rows) render below with a chevron flip.
@@ -38,11 +34,11 @@ export function NavItemDropdownBase({
38
34
  ...rest
39
35
  }: NavItemDropdownBaseProps) {
40
36
  return (
41
- <div className={clsx("flex w-[272px] flex-col", className)}>
37
+ <div className={clsx("flex w-full flex-col", className)}>
42
38
  <button
43
39
  type={type}
44
40
  onClick={onToggle}
45
- className="flex items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2"
41
+ className="flex w-full items-center rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-utility-brand-500 focus-visible:ring-offset-2"
46
42
  {...rest}
47
43
  >
48
44
  <div
@@ -53,17 +49,17 @@ export function NavItemDropdownBase({
53
49
  >
54
50
  <div className="flex min-w-0 flex-1 items-center gap-md">
55
51
  {icon && (
56
- <span className="flex size-5 shrink-0 items-center justify-center text-fg-quaternary [&>svg]:size-5">
52
+ <IconBox size={20} className="text-fg-quaternary">
57
53
  {icon}
58
- </span>
54
+ </IconBox>
59
55
  )}
60
56
  <span className="min-w-0 flex-1 truncate text-left text-sm font-semibold text-text-secondary">
61
57
  {label}
62
58
  </span>
63
59
  </div>
64
- <span className="shrink-0 text-fg-quaternary">
65
- <Chevron up={open} />
66
- </span>
60
+ <IconBox size={16} className="text-fg-quaternary">
61
+ <ChevronDown className={clsx("transition-transform", open && "rotate-180")} />
62
+ </IconBox>
67
63
  </div>
68
64
  </button>
69
65
  {open && (
@@ -1,5 +1,7 @@
1
1
  import { type ButtonHTMLAttributes } from "react";
2
2
  import clsx from "clsx";
3
+ import { Menu02, XClose } from "@borisj74/bv-ds-icons";
4
+ import { IconBox } from "../../internal/iconBox";
3
5
 
4
6
  export interface NavMenuButtonProps
5
7
  extends ButtonHTMLAttributes<HTMLButtonElement> {
@@ -7,18 +9,6 @@ export interface NavMenuButtonProps
7
9
  opened?: boolean;
8
10
  }
9
11
 
10
- const Menu = () => (
11
- <svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden>
12
- <path d="M2.5 10h15M2.5 5h15M2.5 15h15" stroke="currentColor" strokeWidth="1.67" strokeLinecap="round" strokeLinejoin="round" />
13
- </svg>
14
- );
15
-
16
- const XClose = () => (
17
- <svg viewBox="0 0 20 20" fill="none" className="size-5" aria-hidden>
18
- <path d="M15 5 5 15M5 5l10 10" stroke="currentColor" strokeWidth="1.67" strokeLinecap="round" strokeLinejoin="round" />
19
- </svg>
20
- );
21
-
22
12
  /**
23
13
  * Hamburger / collapse toggle for the mobile header & sidebar. Shows the
24
14
  * hamburger when closed, an x-close (dimmed) when `opened`. Brand focus ring.
@@ -41,7 +31,15 @@ export function NavMenuButton({
41
31
  )}
42
32
  {...rest}
43
33
  >
44
- {opened ? <span className="opacity-70"><XClose /></span> : <Menu />}
34
+ {opened ? (
35
+ <IconBox size={20} className="opacity-70">
36
+ <XClose />
37
+ </IconBox>
38
+ ) : (
39
+ <IconBox size={20}>
40
+ <Menu02 />
41
+ </IconBox>
42
+ )}
45
43
  </button>
46
44
  );
47
45
  }
@@ -62,17 +62,22 @@ export function SidebarNavigation({
62
62
  const rail = (
63
63
  <nav
64
64
  className={clsx(
65
+ // Padding is applied per-section (Figma node 1161:8593): header 20px,
66
+ // nav items 16px, footer 16px/20px — not a uniform rail inset, so the
67
+ // full-width nav items align to their own 16px gutter without clipping.
65
68
  "flex h-full flex-col gap-2xl border-r border-border-secondary bg-bg-primary",
66
- slim ? "items-center px-md py-xl" : "px-lg py-xl",
69
+ slim ? "items-center px-md py-xl" : "py-xl",
67
70
  WIDTH[type],
68
71
  className,
69
72
  )}
70
73
  {...rest}
71
74
  >
72
- {logo && <div className={clsx("shrink-0", slim ? "" : "px-md")}>{logo}</div>}
73
- {header && !slim && <div className="shrink-0">{header}</div>}
74
- <div className="flex min-h-0 flex-1 flex-col gap-xxs overflow-y-auto">{children}</div>
75
- {footer && <div className="flex shrink-0 flex-col gap-lg">{footer}</div>}
75
+ {logo && <div className={clsx("shrink-0", slim ? "" : "px-2xl")}>{logo}</div>}
76
+ {header && !slim && <div className="shrink-0 px-2xl">{header}</div>}
77
+ <div className={clsx("flex min-h-0 flex-1 flex-col gap-xxs overflow-y-auto", !slim && "px-xl")}>
78
+ {children}
79
+ </div>
80
+ {footer && <div className={clsx("flex shrink-0 flex-col gap-lg", !slim && "px-xl")}>{footer}</div>}
76
81
  </nav>
77
82
  );
78
83
 
@@ -0,0 +1,36 @@
1
+ import { cloneElement, isValidElement, type ReactElement, type ReactNode } from "react";
2
+ import clsx from "clsx";
3
+
4
+ export interface IconBoxProps {
5
+ /** Box size in px (Figma: 20 for nav icons, 16 for trailing/chevron icons). */
6
+ size?: 16 | 20 | 24;
7
+ className?: string;
8
+ children: ReactNode;
9
+ }
10
+
11
+ /**
12
+ * Sizes an icon to an exact px box and makes it scale correctly.
13
+ *
14
+ * The `@borisj74/bv-ds-icons` package (v0.1.0) ships SVGs with `width="1em"`
15
+ * and **no `viewBox`**, so a 24-unit glyph can't scale into a smaller box and
16
+ * its edges get cropped. We inject `viewBox="0 0 24 24"` (the Untitled UI grid)
17
+ * and `w-full h-full` onto the icon element so it scales into the box cleanly —
18
+ * no `overflow-clip` cropping. Icons inherit `currentColor`.
19
+ */
20
+ export function IconBox({ size = 20, className, children }: IconBoxProps) {
21
+ const icon = isValidElement(children)
22
+ ? cloneElement(children as ReactElement<{ viewBox?: string; className?: string }>, {
23
+ viewBox: (children as ReactElement<{ viewBox?: string }>).props.viewBox ?? "0 0 24 24",
24
+ className: clsx("w-full h-full", (children as ReactElement<{ className?: string }>).props.className),
25
+ })
26
+ : children;
27
+
28
+ return (
29
+ <span
30
+ className={clsx("relative flex shrink-0 items-center justify-center", className)}
31
+ style={{ width: size, height: size }}
32
+ >
33
+ {icon}
34
+ </span>
35
+ );
36
+ }
@@ -0,0 +1,6 @@
1
+ // The @borisj74/bv-ds-icons package (v0.1.0) ships runtime JS but its published
2
+ // tarball is missing the `dist/index.d.ts` its package.json points at, so TS
3
+ // can't resolve types for the icon imports (TS7016). Declare it as an untyped
4
+ // module until the icons package ships its declarations. Icons are simple SVG
5
+ // React components that accept standard SVG props (className, etc.).
6
+ declare module "@borisj74/bv-ds-icons";