@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.esm.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import classNames from 'classnames';
3
- import React, { useEffect, useState, useCallback, useMemo, useContext, createContext, useLayoutEffect, useSyncExternalStore, forwardRef, useId, useRef, isValidElement, cloneElement, createRef } from 'react';
3
+ import React, { useEffect, useState, useCallback, useMemo, useContext, createContext, useLayoutEffect, useSyncExternalStore, createElement, forwardRef, useId, useRef, isValidElement, cloneElement, createRef } from 'react';
4
4
  import { __rest } from 'tslib';
5
5
  import * as LucideIcons from 'lucide-react';
6
+ import { isValidElementType } from 'react-is';
6
7
  import { createPortal } from 'react-dom';
7
8
  import { AnimatePresence, motion } from 'framer-motion';
8
9
  import { fromEvent } from 'file-selector';
9
- import { SplideSlide, Splide } from '@splidejs/react-splide';
10
+ import { SplideSlide, Splide, SplideTrack } from '@splidejs/react-splide';
10
11
  import currency from 'currency.js';
11
12
  import DatePicker from 'react-datepicker';
12
13
  import { getYear, format, getMonth } from 'date-fns';
@@ -309,6 +310,7 @@ const DEFAULT_STATE = {
309
310
  familyPrefix: 'bi-',
310
311
  materialStyle: false,
311
312
  },
313
+ iconRegistry: undefined,
312
314
  iconMap: {
313
315
  x: 'X',
314
316
  xLg: 'X',
@@ -374,11 +376,12 @@ const DContext = createContext(DEFAULT_STATE);
374
376
  * @template T - Map of portal name → payload shape (e.g. `{ modal: { title: string } }`).
375
377
  * Pass it once at the top level: `<DContextProvider<MyPortals> ...>`.
376
378
  */
377
- function DContextProvider({ language = DEFAULT_STATE.language, currency = DEFAULT_STATE.currency, icon = DEFAULT_STATE.icon, iconMap = DEFAULT_STATE.iconMap, portalName = 'd-portal', availablePortals, children, }) {
379
+ 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, }) {
378
380
  const [internalContext, setInternalContext,] = useState({
379
381
  language,
380
382
  currency,
381
383
  icon,
384
+ iconRegistry,
382
385
  iconMap,
383
386
  breakpoints: DEFAULT_STATE.breakpoints,
384
387
  });
@@ -495,17 +498,21 @@ function useResponsiveProp(useListener = false) {
495
498
  return { responsivePropValue };
496
499
  }
497
500
 
501
+ function isIconComponent(value) {
502
+ return typeof value !== 'string' && isValidElementType(value);
503
+ }
498
504
  function DIconBase({ icon, color, style, className, size, useListenerSize = false, hasCircle = false, materialStyle = false, familyClass, familyPrefix, strokeWidth = 2, dataAttributes, }) {
499
505
  // If materialStyle is true, use Material Design icons (legacy)
500
- const useMaterialIcons = materialStyle;
506
+ const isStringIcon = typeof icon === 'string';
507
+ const useMaterialIcons = materialStyle && isStringIcon;
501
508
  // Get Lucide icon component
502
509
  const LucideIcon = useMemo(() => {
503
- if (useMaterialIcons)
510
+ if (!isStringIcon || useMaterialIcons)
504
511
  return null;
505
512
  // Try to find the icon in Lucide (expects PascalCase)
506
513
  const icons = LucideIcons;
507
514
  return icons[icon] || null;
508
- }, [icon, useMaterialIcons]);
515
+ }, [icon, isStringIcon, useMaterialIcons]);
509
516
  const { responsivePropValue } = useResponsiveProp(useListenerSize);
510
517
  const resolvedSize = useMemo(() => {
511
518
  if (!size)
@@ -525,24 +532,35 @@ function DIconBase({ icon, color, style, className, size, useListenerSize = fals
525
532
  }, [resolvedSize]);
526
533
  // Render Material Design icon (legacy support)
527
534
  if (useMaterialIcons) {
528
- return (jsx("i", Object.assign({ className: classNames(generateClasses, familyClass), style: generateStyleVariables }, dataAttributes, { children: icon })));
535
+ return (jsx("i", Object.assign({ className: classNames(generateClasses, familyClass), style: generateStyleVariables }, dataAttributes, { children: isStringIcon ? icon : null })));
536
+ }
537
+ if (isIconComponent(icon)) {
538
+ return (jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, dataAttributes, { children: createElement(icon, {
539
+ width: resolvedSize || 24,
540
+ height: resolvedSize || 24,
541
+ strokeWidth,
542
+ }) })));
529
543
  }
530
544
  // Render Lucide icon
531
545
  if (!LucideIcon) {
532
- if (familyClass && familyPrefix) {
546
+ if (isStringIcon && familyClass && familyPrefix) {
533
547
  return (jsx("i", Object.assign({ className: classNames(generateClasses, familyClass, `${familyPrefix}${icon}`), style: generateStyleVariables }, dataAttributes)));
534
548
  }
535
549
  // eslint-disable-next-line no-console
536
- console.warn(`Icon "${icon}" not found in Lucide. Make sure to use PascalCase names (e.g., "Home", "User", "Settings")`);
550
+ console.warn(`Icon "${String(icon)}" not found in Lucide. Make sure to use PascalCase names (e.g., "Home", "User", "Settings")`);
537
551
  return (jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, dataAttributes, { children: "?" })));
538
552
  }
539
553
  return (jsx("span", Object.assign({ className: classNames(generateClasses), style: generateStyleVariables }, dataAttributes, { children: jsx(LucideIcon, { size: iconSize || 24, strokeWidth: strokeWidth }) })));
540
554
  }
541
555
 
542
556
  function DIcon(_a) {
543
- var { familyClass: propFamilyClass, familyPrefix: propFamilyPrefix, materialStyle: propMaterialStyle } = _a, props = __rest(_a, ["familyClass", "familyPrefix", "materialStyle"]);
544
- const { icon: { familyClass, familyPrefix, materialStyle, }, } = useDContext();
545
- return (jsx(DIconBase, Object.assign({ familyClass: propFamilyClass || familyClass, familyPrefix: propFamilyPrefix || familyPrefix, materialStyle: propMaterialStyle || materialStyle }, props)));
557
+ var { icon, familyClass: propFamilyClass, familyPrefix: propFamilyPrefix, materialStyle: propMaterialStyle } = _a, props = __rest(_a, ["icon", "familyClass", "familyPrefix", "materialStyle"]);
558
+ const { icon: { familyClass, familyPrefix, materialStyle, }, iconRegistry, } = useDContext();
559
+ const registryIcon = typeof icon === 'string'
560
+ ? iconRegistry === null || iconRegistry === void 0 ? void 0 : iconRegistry[icon]
561
+ : undefined;
562
+ const resolvedIcon = registryIcon || icon;
563
+ return (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)));
546
564
  }
547
565
 
548
566
  function DAlert({ color = 'success', icon: iconProp, iconFamilyClass, iconFamilyPrefix, iconMaterialStyle = false, iconClose: iconCloseProp, iconCloseFamilyClass, iconCloseFamilyPrefix, iconCloseMaterialStyle = false, showClose, onClose, children, id, className, style, dataAttributes, }) {
@@ -1222,8 +1240,14 @@ function DCarouselSlide(_a) {
1222
1240
  return (jsx(SplideSlide, Object.assign({ className: classNames('d-carousel-slide', className) }, props)));
1223
1241
  }
1224
1242
 
1243
+ 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';
1244
+ function DefaultArrowIcon() {
1245
+ return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 40 40", width: 40, height: 40, focusable: "false", "aria-hidden": "true", children: jsx("path", { d: DEFAULT_ARROW_PATH }) }));
1246
+ }
1225
1247
  function DCarousel(_a, ref) {
1226
- var { children, className, style, options, dataAttributes } = _a, props = __rest(_a, ["children", "className", "style", "options", "dataAttributes"]);
1248
+ var { children, className, style, options, dataAttributes, iconArrowLeft, iconArrowRight, hasTrack: propsHasTrack } = _a, props = __rest(_a, ["children", "className", "style", "options", "dataAttributes", "iconArrowLeft", "iconArrowRight", "hasTrack"]);
1249
+ // Explicit `options.arrows === false` always wins, even when icon props are set.
1250
+ const hasCustomArrows = Boolean((iconArrowLeft || iconArrowRight) && (options === null || options === void 0 ? void 0 : options.arrows) !== false);
1227
1251
  return (jsx(Splide, Object.assign({ className: classNames('d-carousel', className), style: style, ref: ref, options: Object.assign(Object.assign({}, options), { classes: {
1228
1252
  // Arrows
1229
1253
  arrows: 'splide__arrows d-carousel-arrows',
@@ -1233,7 +1257,11 @@ function DCarousel(_a, ref) {
1233
1257
  // Paginator
1234
1258
  pagination: 'splide__pagination d-carousel-pagination',
1235
1259
  page: 'splide__pagination__page d-carousel-pagination-page',
1236
- } }) }, dataAttributes, props, { children: children })));
1260
+ } }) }, dataAttributes, props, {
1261
+ // Rendering our own arrows requires taking over the track wrapper, so this
1262
+ // must be applied after `...props` to prevent a consumer-provided `hasTrack`
1263
+ // from re-enabling Splide's automatic track when custom arrows are active.
1264
+ hasTrack: hasCustomArrows ? false : propsHasTrack, children: hasCustomArrows ? (jsxs(Fragment, { children: [jsx(SplideTrack, { children: children }), jsxs("div", { className: "splide__arrows d-carousel-arrows", children: [jsx("button", { type: "button", className: "splide__arrow splide__arrow--prev d-carousel-arrow d-carousel-arrow-prev", children: iconArrowLeft ? jsx(DIcon, Object.assign({}, iconArrowLeft)) : jsx(DefaultArrowIcon, {}) }), jsx("button", { type: "button", className: "splide__arrow splide__arrow--next d-carousel-arrow d-carousel-arrow-next", children: iconArrowRight ? jsx(DIcon, Object.assign({}, iconArrowRight)) : jsx(DefaultArrowIcon, {}) })] })] })) : children })));
1237
1265
  }
1238
1266
  const ForwardedDCarousel = forwardRef(DCarousel);
1239
1267
  ForwardedDCarousel.displayName = 'DCarousel';
@@ -2296,10 +2324,37 @@ const defaultTransition = {
2296
2324
  ease: 'easeInOut',
2297
2325
  duration: 0.3,
2298
2326
  };
2299
- function DOffcanvas({ name, className, style, staticBackdrop, scrollable, openFrom = 'end', transition, children, dataAttributes, }) {
2327
+ function DOffcanvas({ name, className, style, staticBackdrop, scrollable, openFrom = 'end', width, height, transition, children, dataAttributes, }) {
2328
+ // Only subscribe to breakpoint-change listeners when a responsive object is
2329
+ // actually provided, avoiding unnecessary matchMedia subscriptions for the
2330
+ // common case of plain string values.
2331
+ const hasResponsiveProp = typeof openFrom === 'object'
2332
+ || typeof width === 'object'
2333
+ || typeof height === 'object';
2334
+ const { responsivePropValue } = useResponsiveProp(hasResponsiveProp);
2335
+ const resolvedOpenFrom = useMemo(() => {
2336
+ var _a;
2337
+ if (typeof openFrom === 'string')
2338
+ return openFrom;
2339
+ return (_a = responsivePropValue(openFrom)) !== null && _a !== void 0 ? _a : 'end';
2340
+ }, [responsivePropValue, openFrom]);
2341
+ const resolvedWidth = useMemo(() => {
2342
+ if (!width)
2343
+ return undefined;
2344
+ if (typeof width === 'string')
2345
+ return width;
2346
+ return responsivePropValue(width);
2347
+ }, [responsivePropValue, width]);
2348
+ const resolvedHeight = useMemo(() => {
2349
+ if (!height)
2350
+ return undefined;
2351
+ if (typeof height === 'string')
2352
+ return height;
2353
+ return responsivePropValue(height);
2354
+ }, [responsivePropValue, height]);
2300
2355
  return (jsx(motion.div, Object.assign({ className: classNames('offcanvas portal show', {
2301
- [`offcanvas-${openFrom}`]: openFrom,
2302
- }, 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 && ({
2356
+ [`offcanvas-${resolvedOpenFrom}`]: resolvedOpenFrom,
2357
+ }, 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 && ({
2303
2358
  [`data-${PREFIX_BS}backdrop`]: 'static',
2304
2359
  [`data-${PREFIX_BS}keyboard`]: 'false',
2305
2360
  }), scrollable && ({