@dynamic-framework/ui-react 2.4.0 → 2.5.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
@@ -73,6 +73,12 @@ function useDisableBodyScrollEffect(disable) {
73
73
  }, [disable]);
74
74
  }
75
75
 
76
+ /**
77
+ * @internal
78
+ * Creates a DOM `<div>` portal mount point and appends it to `document.body`.
79
+ * This hook is used exclusively by `DPortalContextProvider` and is **not** part
80
+ * of the public API. Use `useDPortalContext` instead.
81
+ */
76
82
  function usePortal(portalName) {
77
83
  const [hasPortal, setHasPortal] = useState(false);
78
84
  useEffect(() => {
@@ -139,20 +145,22 @@ function DPortalContextProvider({ portalName, children, availablePortals, }) {
139
145
  const { created } = usePortal(portalName);
140
146
  const [stack, { push, pop, isEmpty }] = useStackState([]);
141
147
  useDisableBodyScrollEffect(Boolean(stack.length));
142
- const openPortal = useCallback((name, payload) => {
148
+ const openPortal = useCallback(
149
+ // eslint-disable-next-line prefer-arrow-callback
150
+ function openPortalImpl(name, payload) {
143
151
  var _a;
144
152
  if (!availablePortals) {
145
- throw new Error(`there is no component for portal ${name.toString()}`);
153
+ throw new Error('openPortal was called but DContextProvider has no availablePortals configured. '
154
+ + 'Pass an availablePortals map to DContextProvider.');
146
155
  }
147
156
  const Component = availablePortals[name];
148
157
  if (!Component) {
149
- throw new Error(`there is no component for portal ${name.toString()}`);
158
+ throw new Error(`No component registered for portal "${String(name)}". `
159
+ + `Ensure "${String(name)}" has an entry in the availablePortals map on DContextProvider.`);
150
160
  }
151
- push({
152
- name,
153
- Component,
154
- payload,
155
- });
161
+ // K is a specific member of keyof T & string so the object satisfies
162
+ // InternalStackItem<T>, but TS can't verify generic-over-union assignability.
163
+ push({ name, Component, payload });
156
164
  (_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.blur();
157
165
  }, [availablePortals, push]);
158
166
  const closePortal = useCallback(() => {
@@ -161,11 +169,12 @@ function DPortalContextProvider({ portalName, children, availablePortals, }) {
161
169
  }
162
170
  pop();
163
171
  }, [isEmpty, pop]);
172
+ const publicStack = useMemo(() => stack.map(({ name, payload }) => ({ name, payload })), [stack]);
164
173
  const value = useMemo(() => ({
165
- stack,
174
+ stack: publicStack,
166
175
  openPortal,
167
176
  closePortal,
168
- }), [stack, openPortal, closePortal]);
177
+ }), [publicStack, openPortal, closePortal]);
169
178
  const handleClose = useCallback((target) => {
170
179
  if (!(target instanceof HTMLDivElement)) {
171
180
  return;
@@ -221,10 +230,31 @@ function DPortalContextProvider({ portalName, children, availablePortals, }) {
221
230
  jsx(Component, { name: name, payload: payload }, name),
222
231
  ]) }) }), document.getElementById(portalName))] }));
223
232
  }
233
+ /**
234
+ * Hook to open/close registered portals (modals, offcanvas, etc.).
235
+ *
236
+ * **Prerequisite**: must be called inside a `DContextProvider` configured with
237
+ * `portalName` and `availablePortals`. `DContextProvider` mounts
238
+ * `DPortalContextProvider` internally — consumers never use
239
+ * `DPortalContextProvider` directly.
240
+ *
241
+ * @template T - Map of portal name → payload shape (e.g. `ModalPayloads`).
242
+ * Typing this generic gives you autocomplete on `openPortal` arguments.
243
+ * @returns `{ openPortal, closePortal, stack }` from the nearest portal context.
244
+ * @throws If called outside of `DContextProvider` / `DPortalContextProvider`.
245
+ *
246
+ * @requires DContextProvider
247
+ *
248
+ * @example
249
+ * ```tsx
250
+ * const { openPortal, closePortal } = useDPortalContext<ModalPayloads>();
251
+ * openPortal('confirm', { message: 'Are you sure?' });
252
+ * ```
253
+ */
224
254
  function useDPortalContext() {
225
255
  const context = useContext(DPortalContext);
226
256
  if (context === undefined) {
227
- throw new Error('usePortalContext was used outside of PortalContextProvider');
257
+ throw new Error('useDPortalContext was used outside of DPortalContextProvider');
228
258
  }
229
259
  return context;
230
260
  }
@@ -280,10 +310,17 @@ const DEFAULT_STATE = {
280
310
  xxl: '',
281
311
  },
282
312
  setContext: () => { },
283
- portalName: 'd-portal',
284
313
  };
285
314
  const DContext = createContext(DEFAULT_STATE);
286
- function DContextProvider({ language = DEFAULT_STATE.language, currency = DEFAULT_STATE.currency, icon = DEFAULT_STATE.icon, iconMap = DEFAULT_STATE.iconMap, portalName = DEFAULT_STATE.portalName, availablePortals, children, }) {
315
+ /**
316
+ * Root context provider for Dynamic UI. Wrap your application with this
317
+ * component to configure icons, currency, language, and portal settings
318
+ * for all descendant Dynamic UI components.
319
+ *
320
+ * @template T - Map of portal name → payload shape (e.g. `{ modal: { title: string } }`).
321
+ * Pass it once at the top level: `<DContextProvider<MyPortals> ...>`.
322
+ */
323
+ function DContextProvider({ language = DEFAULT_STATE.language, currency = DEFAULT_STATE.currency, icon = DEFAULT_STATE.icon, iconMap = DEFAULT_STATE.iconMap, portalName = 'd-portal', availablePortals, children, }) {
287
324
  const [internalContext, setInternalContext,] = useState({
288
325
  language,
289
326
  currency,
@@ -307,12 +344,14 @@ function DContextProvider({ language = DEFAULT_STATE.language, currency = DEFAUL
307
344
  const value = useMemo(() => (Object.assign(Object.assign({}, internalContext), { setContext })), [internalContext, setContext]);
308
345
  return (jsx(DContext.Provider, { value: value, children: jsx(DPortalContextProvider, { portalName: portalName, availablePortals: availablePortals, children: children }) }));
309
346
  }
347
+ /**
348
+ * Returns the Dynamic UI context value set by `DContextProvider`.
349
+ * Falls back to the library's built-in defaults when no `DContextProvider`
350
+ * is present in the tree — wrap your application with `DContextProvider`
351
+ * to customise icons, currency, language, and portal settings.
352
+ */
310
353
  function useDContext() {
311
- const context = useContext(DContext);
312
- if (context === undefined) {
313
- throw new Error('useDContext was used outside of DContextProvider');
314
- }
315
- return context;
354
+ return useContext(DContext);
316
355
  }
317
356
 
318
357
  function subscribeToMediaQuery(query, callback) {
@@ -2379,12 +2418,34 @@ var DToast$1 = Object.assign(DToast, {
2379
2418
  Body: DToastBody,
2380
2419
  });
2381
2420
 
2382
- function DToastContainer({ containerClassName, position = 'bottom-center', reverseOrder = false, containerStyle, toastOptions, gutter, }) {
2421
+ function DToastContainer({ containerClassName, position = 'bottom-center', reverseOrder = false, containerStyle, toastOptions, gutter = 8, }) {
2383
2422
  return (jsx(Toaster, { containerClassName: containerClassName, position: position, reverseOrder: reverseOrder, containerStyle: containerStyle, gutter: gutter, toastOptions: toastOptions }));
2384
2423
  }
2385
2424
 
2425
+ /**
2426
+ * Hook that provides a `toast` function to dispatch DToast notifications.
2427
+ *
2428
+ * Prerequisites:
2429
+ * - The calling component must be inside `DContextProvider` (provides icon context).
2430
+ * - `DToastContainer` must be present somewhere in the component tree as a render target.
2431
+ *
2432
+ * @returns {{ toast }} Object containing a `toast` dispatcher function.
2433
+ * @requires DContextProvider
2434
+ * @requires DToastContainer
2435
+ * @example
2436
+ * const { toast } = useDToast();
2437
+ * toast({ title: 'Saved', color: 'success' });
2438
+ */
2386
2439
  function useDToast() {
2387
2440
  const { iconMap: { xLg, }, } = useDContext();
2441
+ /**
2442
+ * Dispatches a toast notification rendered as a `DToast` component.
2443
+ * When `data` is a `ToastData` object, the default DToast layout is used.
2444
+ * When `data` is a render function, full custom content is rendered instead.
2445
+ * Returns the toast ID, which can be passed to `reactHotToast.dismiss(id)`.
2446
+ * @param data - Toast content as `ToastData` or a render function for custom layouts.
2447
+ * @param toastProps - Optional per-toast overrides: id, duration, position.
2448
+ */
2388
2449
  const toast$1 = useCallback((data, toastProps) => {
2389
2450
  if (typeof data === 'function') {
2390
2451
  return toast.custom(data, toastProps);
@@ -2551,11 +2612,14 @@ function DCreditCard({ brand = 'visa', name, number, holderText = 'Card Holder',
2551
2612
  }
2552
2613
 
2553
2614
  const getItemClass = (action) => {
2554
- const base = `dropdown-item d-flex align-items-center
2555
- ${action.color ? `dropdown-item-${action.color}` : ''} ${action.disabled ? 'disabled' : ''}`;
2615
+ const base = classNames({
2616
+ 'dropdown-item d-flex align-items-center': true,
2617
+ [`dropdown-item-${action.color}`]: !!action.color,
2618
+ disabled: action.disabled,
2619
+ });
2556
2620
  return base;
2557
2621
  };
2558
- function DDropdown({ actions, dropdownToggle, className, }) {
2622
+ function DDropdown({ actions, dropdownToggle, className, classNameMenu, }) {
2559
2623
  const [open, setOpen] = useState(false);
2560
2624
  const dropdownRef = useRef(null);
2561
2625
  const [position, setPosition] = useState('down'); // 🆕
@@ -2597,14 +2661,14 @@ function DDropdown({ actions, dropdownToggle, className, }) {
2597
2661
  else {
2598
2662
  ToggleElement = (jsx(DButtonIcon, { variant: "link", stopPropagationEnabled: false, "aria-label": "Toggle Dropdown", "aria-haspopup": "menu", "aria-expanded": open, onClick: () => setOpen(!open), icon: "MoreVertical" }));
2599
2663
  }
2600
- return (jsxs("div", { className: `dropdown position-relative drop-${position} ${className}`, ref: dropdownRef, children: [ToggleElement, jsx("ul", { style: {
2664
+ return (jsxs("div", { className: classNames(`dropdown drop-${position}`, className), ref: dropdownRef, children: [ToggleElement, jsx("ul", { style: {
2601
2665
  position: 'absolute',
2602
2666
  top: position === 'up' ? 'auto' : '100%',
2603
2667
  bottom: position === 'up' ? '100%' : 'auto',
2604
2668
  left: position === 'start' ? 'auto' : 0,
2605
2669
  right: position === 'start' ? '0' : 'auto',
2606
2670
  transform: 'translateY(4px)',
2607
- }, className: `dropdown-menu p-2 ${open ? 'show' : ''}`, children: actions.map((action, index) => {
2671
+ }, className: classNames('dropdown-menu p-2', { show: open }, classNameMenu), children: actions.map((action, index) => {
2608
2672
  if (action.isDivider) {
2609
2673
  return (jsx("hr", { className: "dropdown-divider" }, index));
2610
2674
  }
@@ -2861,5 +2925,5 @@ function DDataStateWrapper({ isLoading, isError, data, onRetry, renderLoading, r
2861
2925
  return jsx(Fragment, { children: children(data) });
2862
2926
  }
2863
2927
 
2864
- export { DAlert, DAvatar, DBadge, DBox, DBoxFile, DButton, DButtonIcon, DCard$1 as DCard, DCardBody, DCardFooter, DCardHeader, DCarousel$1 as DCarousel, DCarouselSlide, DChip, DCollapse, DContext, DContextProvider, DCreditCard, DCurrencyText, DDataStateWrapper, DDatePicker, DDropdown, DErrorBoundary, DIcon, DIconBase, ForwardedDInput as DInput, DInputCheck, ForwardedDInputCounter as DInputCounter, ForwardedDInputCurrency as DInputCurrency, ForwardedDInputMask as DInputMask, ForwardedDInputPassword as DInputPassword, ForwardedDInputPhone as DInputPhone, DInputPin, ForwardedDInputRange as DInputRange, DInputSelect, DInputSwitch, DLayout$1 as DLayout, DLayoutPane, DListGroup$1 as DListGroup, DListGroupItem, DModal$1 as DModal, DModalBody, DModalFooter, DModalHeader, DOffcanvas$1 as DOffcanvas, DOffcanvasBody, DOffcanvasFooter, DOffcanvasHeader, DOtp, DPaginator, DPasswordStrengthMeter, DPopover, DProgress, DSelect$1 as DSelect, DStepper, DStepper$2 as DStepperDesktop, DStepper$1 as DStepperMobile, DTabContent, DTabs$1 as DTabs, DTimeline, DToast$1 as DToast, DToastContainer, DTooltip, DVoucher, changeQueryString, checkMediaQuery, configureI8n as configureI18n, formatCurrency, getCssVariable, getQueryString, subscribeToMediaQuery, useDContext, useDPortalContext, useDToast, useDisableBodyScrollEffect, useDisableInputWheel, useFormatCurrency, useInputCurrency, useItemSelection, useMediaBreakpointUpLg, useMediaBreakpointUpMd, useMediaBreakpointUpSm, useMediaBreakpointUpXl, useMediaBreakpointUpXs, useMediaBreakpointUpXxl, useMediaQuery, usePortal, useProvidedRefOrCreate, useStackState, useTabContext, validatePhoneNumber };
2928
+ export { DAlert, DAvatar, DBadge, DBox, DBoxFile, DButton, DButtonIcon, DCard$1 as DCard, DCardBody, DCardFooter, DCardHeader, DCarousel$1 as DCarousel, DCarouselSlide, DChip, DCollapse, DContext, DContextProvider, DCreditCard, DCurrencyText, DDataStateWrapper, DDatePicker, DDropdown, DErrorBoundary, DIcon, DIconBase, ForwardedDInput as DInput, DInputCheck, ForwardedDInputCounter as DInputCounter, ForwardedDInputCurrency as DInputCurrency, ForwardedDInputMask as DInputMask, ForwardedDInputPassword as DInputPassword, ForwardedDInputPhone as DInputPhone, DInputPin, ForwardedDInputRange as DInputRange, DInputSelect, DInputSwitch, DLayout$1 as DLayout, DLayoutPane, DListGroup$1 as DListGroup, DListGroupItem, DModal$1 as DModal, DModalBody, DModalFooter, DModalHeader, DOffcanvas$1 as DOffcanvas, DOffcanvasBody, DOffcanvasFooter, DOffcanvasHeader, DOtp, DPaginator, DPasswordStrengthMeter, DPopover, DProgress, DSelect$1 as DSelect, DStepper, DStepper$2 as DStepperDesktop, DStepper$1 as DStepperMobile, DTabContent, DTabs$1 as DTabs, DTimeline, DToast$1 as DToast, DToastContainer, DTooltip, DVoucher, changeQueryString, checkMediaQuery, configureI8n as configureI18n, formatCurrency, getCssVariable, getQueryString, subscribeToMediaQuery, useDContext, useDPortalContext, useDToast, useDisableBodyScrollEffect, useDisableInputWheel, useFormatCurrency, useInputCurrency, useItemSelection, useMediaBreakpointUpLg, useMediaBreakpointUpMd, useMediaBreakpointUpSm, useMediaBreakpointUpXl, useMediaBreakpointUpXs, useMediaBreakpointUpXxl, useMediaQuery, useProvidedRefOrCreate, useStackState, useTabContext, validatePhoneNumber };
2865
2929
  //# sourceMappingURL=index.esm.js.map