@dynamic-framework/ui-react 2.6.0 → 2.7.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/index.js CHANGED
@@ -5,6 +5,7 @@ var classNames = require('classnames');
5
5
  var React = require('react');
6
6
  var tslib = require('tslib');
7
7
  var LucideIcons = require('lucide-react');
8
+ var reactIs = require('react-is');
8
9
  var reactDom = require('react-dom');
9
10
  var framerMotion = require('framer-motion');
10
11
  var fileSelector = require('file-selector');
@@ -329,6 +330,7 @@ const DEFAULT_STATE = {
329
330
  familyPrefix: 'bi-',
330
331
  materialStyle: false,
331
332
  },
333
+ iconRegistry: undefined,
332
334
  iconMap: {
333
335
  x: 'X',
334
336
  xLg: 'X',
@@ -394,11 +396,12 @@ const DContext = React.createContext(DEFAULT_STATE);
394
396
  * @template T - Map of portal name → payload shape (e.g. `{ modal: { title: string } }`).
395
397
  * Pass it once at the top level: `<DContextProvider<MyPortals> ...>`.
396
398
  */
397
- function DContextProvider({ language = DEFAULT_STATE.language, currency = DEFAULT_STATE.currency, icon = DEFAULT_STATE.icon, iconMap = DEFAULT_STATE.iconMap, portalName = 'd-portal', availablePortals, children, }) {
399
+ function DContextProvider({ language = DEFAULT_STATE.language, currency = DEFAULT_STATE.currency, icon = DEFAULT_STATE.icon, iconRegistry = DEFAULT_STATE.iconRegistry, iconMap = DEFAULT_STATE.iconMap, portalName = 'd-portal', availablePortals, children, }) {
398
400
  const [internalContext, setInternalContext,] = React.useState({
399
401
  language,
400
402
  currency,
401
403
  icon,
404
+ iconRegistry,
402
405
  iconMap,
403
406
  breakpoints: DEFAULT_STATE.breakpoints,
404
407
  });
@@ -515,17 +518,21 @@ function useResponsiveProp(useListener = false) {
515
518
  return { responsivePropValue };
516
519
  }
517
520
 
521
+ function isIconComponent(value) {
522
+ return typeof value !== 'string' && reactIs.isValidElementType(value);
523
+ }
518
524
  function DIconBase({ icon, color, style, className, size, useListenerSize = false, hasCircle = false, materialStyle = false, familyClass, familyPrefix, strokeWidth = 2, dataAttributes, }) {
519
525
  // If materialStyle is true, use Material Design icons (legacy)
520
- const useMaterialIcons = materialStyle;
526
+ const isStringIcon = typeof icon === 'string';
527
+ const useMaterialIcons = materialStyle && isStringIcon;
521
528
  // Get Lucide icon component
522
529
  const LucideIcon = React.useMemo(() => {
523
- if (useMaterialIcons)
530
+ if (!isStringIcon || useMaterialIcons)
524
531
  return null;
525
532
  // Try to find the icon in Lucide (expects PascalCase)
526
533
  const icons = LucideIcons__namespace;
527
534
  return icons[icon] || null;
528
- }, [icon, useMaterialIcons]);
535
+ }, [icon, isStringIcon, useMaterialIcons]);
529
536
  const { responsivePropValue } = useResponsiveProp(useListenerSize);
530
537
  const resolvedSize = React.useMemo(() => {
531
538
  if (!size)
@@ -545,24 +552,35 @@ function DIconBase({ icon, color, style, className, size, useListenerSize = fals
545
552
  }, [resolvedSize]);
546
553
  // Render Material Design icon (legacy support)
547
554
  if (useMaterialIcons) {
548
- return (jsxRuntime.jsx("i", Object.assign({ className: classNames(generateClasses, familyClass), style: generateStyleVariables }, dataAttributes, { children: icon })));
555
+ return (jsxRuntime.jsx("i", Object.assign({ className: classNames(generateClasses, familyClass), style: generateStyleVariables }, dataAttributes, { children: isStringIcon ? icon : null })));
556
+ }
557
+ if (isIconComponent(icon)) {
558
+ return (jsxRuntime.jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, dataAttributes, { children: React.createElement(icon, {
559
+ width: resolvedSize || 24,
560
+ height: resolvedSize || 24,
561
+ strokeWidth,
562
+ }) })));
549
563
  }
550
564
  // Render Lucide icon
551
565
  if (!LucideIcon) {
552
- if (familyClass && familyPrefix) {
566
+ if (isStringIcon && familyClass && familyPrefix) {
553
567
  return (jsxRuntime.jsx("i", Object.assign({ className: classNames(generateClasses, familyClass, `${familyPrefix}${icon}`), style: generateStyleVariables }, dataAttributes)));
554
568
  }
555
569
  // eslint-disable-next-line no-console
556
- console.warn(`Icon "${icon}" not found in Lucide. Make sure to use PascalCase names (e.g., "Home", "User", "Settings")`);
570
+ console.warn(`Icon "${String(icon)}" not found in Lucide. Make sure to use PascalCase names (e.g., "Home", "User", "Settings")`);
557
571
  return (jsxRuntime.jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, dataAttributes, { children: "?" })));
558
572
  }
559
573
  return (jsxRuntime.jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, dataAttributes, { children: jsxRuntime.jsx(LucideIcon, { size: iconSize || 24, strokeWidth: strokeWidth }) })));
560
574
  }
561
575
 
562
576
  function DIcon(_a) {
563
- var { familyClass: propFamilyClass, familyPrefix: propFamilyPrefix, materialStyle: propMaterialStyle } = _a, props = tslib.__rest(_a, ["familyClass", "familyPrefix", "materialStyle"]);
564
- const { icon: { familyClass, familyPrefix, materialStyle, }, } = useDContext();
565
- return (jsxRuntime.jsx(DIconBase, Object.assign({ familyClass: propFamilyClass || familyClass, familyPrefix: propFamilyPrefix || familyPrefix, materialStyle: propMaterialStyle || materialStyle }, props)));
577
+ var { icon, familyClass: propFamilyClass, familyPrefix: propFamilyPrefix, materialStyle: propMaterialStyle } = _a, props = tslib.__rest(_a, ["icon", "familyClass", "familyPrefix", "materialStyle"]);
578
+ const { icon: { familyClass, familyPrefix, materialStyle, }, iconRegistry, } = useDContext();
579
+ const registryIcon = typeof icon === 'string'
580
+ ? iconRegistry === null || iconRegistry === void 0 ? void 0 : iconRegistry[icon]
581
+ : undefined;
582
+ const resolvedIcon = registryIcon || icon;
583
+ return (jsxRuntime.jsx(DIconBase, Object.assign({ icon: resolvedIcon, familyClass: propFamilyClass !== null && propFamilyClass !== void 0 ? propFamilyClass : familyClass, familyPrefix: propFamilyPrefix !== null && propFamilyPrefix !== void 0 ? propFamilyPrefix : familyPrefix, materialStyle: propMaterialStyle !== null && propMaterialStyle !== void 0 ? propMaterialStyle : materialStyle }, props)));
566
584
  }
567
585
 
568
586
  function DAlert({ color = 'success', icon: iconProp, iconFamilyClass, iconFamilyPrefix, iconMaterialStyle = false, iconClose: iconCloseProp, iconCloseFamilyClass, iconCloseFamilyPrefix, iconCloseMaterialStyle = false, showClose, onClose, children, id, className, style, dataAttributes, }) {
@@ -1242,8 +1260,14 @@ function DCarouselSlide(_a) {
1242
1260
  return (jsxRuntime.jsx(reactSplide.SplideSlide, Object.assign({ className: classNames('d-carousel-slide', className) }, props)));
1243
1261
  }
1244
1262
 
1263
+ const DEFAULT_ARROW_PATH = 'm15.5 0.932-4.3 4.38 14.5 14.6-14.5 14.5 4.3 4.4 14.6-14.6 4.4-4.3-4.4-4.4-14.6-14.6z';
1264
+ function DefaultArrowIcon() {
1265
+ return (jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 40 40", width: 40, height: 40, focusable: "false", "aria-hidden": "true", children: jsxRuntime.jsx("path", { d: DEFAULT_ARROW_PATH }) }));
1266
+ }
1245
1267
  function DCarousel$1(_a, ref) {
1246
- var { children, className, style, options, dataAttributes } = _a, props = tslib.__rest(_a, ["children", "className", "style", "options", "dataAttributes"]);
1268
+ var { children, className, style, options, dataAttributes, iconArrowLeft, iconArrowRight, hasTrack: propsHasTrack } = _a, props = tslib.__rest(_a, ["children", "className", "style", "options", "dataAttributes", "iconArrowLeft", "iconArrowRight", "hasTrack"]);
1269
+ // Explicit `options.arrows === false` always wins, even when icon props are set.
1270
+ const hasCustomArrows = Boolean((iconArrowLeft || iconArrowRight) && (options === null || options === void 0 ? void 0 : options.arrows) !== false);
1247
1271
  return (jsxRuntime.jsx(reactSplide.Splide, Object.assign({ className: classNames('d-carousel', className), style: style, ref: ref, options: Object.assign(Object.assign({}, options), { classes: {
1248
1272
  // Arrows
1249
1273
  arrows: 'splide__arrows d-carousel-arrows',
@@ -1253,7 +1277,11 @@ function DCarousel$1(_a, ref) {
1253
1277
  // Paginator
1254
1278
  pagination: 'splide__pagination d-carousel-pagination',
1255
1279
  page: 'splide__pagination__page d-carousel-pagination-page',
1256
- } }) }, dataAttributes, props, { children: children })));
1280
+ } }) }, dataAttributes, props, {
1281
+ // Rendering our own arrows requires taking over the track wrapper, so this
1282
+ // must be applied after `...props` to prevent a consumer-provided `hasTrack`
1283
+ // from re-enabling Splide's automatic track when custom arrows are active.
1284
+ hasTrack: hasCustomArrows ? false : propsHasTrack, children: hasCustomArrows ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(reactSplide.SplideTrack, { children: children }), jsxRuntime.jsxs("div", { className: "splide__arrows d-carousel-arrows", children: [jsxRuntime.jsx("button", { type: "button", className: "splide__arrow splide__arrow--prev d-carousel-arrow d-carousel-arrow-prev", children: iconArrowLeft ? jsxRuntime.jsx(DIcon, Object.assign({}, iconArrowLeft)) : jsxRuntime.jsx(DefaultArrowIcon, {}) }), jsxRuntime.jsx("button", { type: "button", className: "splide__arrow splide__arrow--next d-carousel-arrow d-carousel-arrow-next", children: iconArrowRight ? jsxRuntime.jsx(DIcon, Object.assign({}, iconArrowRight)) : jsxRuntime.jsx(DefaultArrowIcon, {}) })] })] })) : children })));
1257
1285
  }
1258
1286
  const ForwardedDCarousel = React.forwardRef(DCarousel$1);
1259
1287
  ForwardedDCarousel.displayName = 'DCarousel';
@@ -2316,10 +2344,37 @@ const defaultTransition = {
2316
2344
  ease: 'easeInOut',
2317
2345
  duration: 0.3,
2318
2346
  };
2319
- function DOffcanvas$1({ name, className, style, staticBackdrop, scrollable, openFrom = 'end', transition, children, dataAttributes, }) {
2347
+ function DOffcanvas$1({ name, className, style, staticBackdrop, scrollable, openFrom = 'end', width, height, transition, children, dataAttributes, }) {
2348
+ // Only subscribe to breakpoint-change listeners when a responsive object is
2349
+ // actually provided, avoiding unnecessary matchMedia subscriptions for the
2350
+ // common case of plain string values.
2351
+ const hasResponsiveProp = typeof openFrom === 'object'
2352
+ || typeof width === 'object'
2353
+ || typeof height === 'object';
2354
+ const { responsivePropValue } = useResponsiveProp(hasResponsiveProp);
2355
+ const resolvedOpenFrom = React.useMemo(() => {
2356
+ var _a;
2357
+ if (typeof openFrom === 'string')
2358
+ return openFrom;
2359
+ return (_a = responsivePropValue(openFrom)) !== null && _a !== void 0 ? _a : 'end';
2360
+ }, [responsivePropValue, openFrom]);
2361
+ const resolvedWidth = React.useMemo(() => {
2362
+ if (!width)
2363
+ return undefined;
2364
+ if (typeof width === 'string')
2365
+ return width;
2366
+ return responsivePropValue(width);
2367
+ }, [responsivePropValue, width]);
2368
+ const resolvedHeight = React.useMemo(() => {
2369
+ if (!height)
2370
+ return undefined;
2371
+ if (typeof height === 'string')
2372
+ return height;
2373
+ return responsivePropValue(height);
2374
+ }, [responsivePropValue, height]);
2320
2375
  return (jsxRuntime.jsx(framerMotion.motion.div, Object.assign({ className: classNames('offcanvas portal show', {
2321
- [`offcanvas-${openFrom}`]: openFrom,
2322
- }, className), style: Object.assign(Object.assign({}, style), { transition: 'none' }), id: name, tabIndex: -1, "aria-labelledby": `${name}Label`, "aria-hidden": "false", custom: openFrom, variants: variants, initial: "hidden", animate: "visible", exit: "hidden", transition: Object.assign(Object.assign({}, (transition !== null && transition !== void 0 ? transition : defaultTransition)), { delay: 0.15 }) }, staticBackdrop && ({
2376
+ [`offcanvas-${resolvedOpenFrom}`]: resolvedOpenFrom,
2377
+ }, className), style: Object.assign(Object.assign(Object.assign(Object.assign({}, style), { transition: 'none' }), (resolvedWidth && { [`--${PREFIX_BS}offcanvas-width`]: resolvedWidth })), (resolvedHeight && { [`--${PREFIX_BS}offcanvas-height`]: resolvedHeight })), id: name, tabIndex: -1, "aria-labelledby": `${name}Label`, "aria-hidden": "false", custom: resolvedOpenFrom, variants: variants, initial: "hidden", animate: "visible", exit: "hidden", transition: Object.assign(Object.assign({}, (transition !== null && transition !== void 0 ? transition : defaultTransition)), { delay: 0.15 }) }, staticBackdrop && ({
2323
2378
  [`data-${PREFIX_BS}backdrop`]: 'static',
2324
2379
  [`data-${PREFIX_BS}keyboard`]: 'false',
2325
2380
  }), scrollable && ({