@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/css/dynamic-ui.css +14168 -7314
- package/dist/css/dynamic-ui.min.css +2 -2
- package/dist/index.esm.js +89 -25
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +88 -25
- package/dist/index.js.map +1 -1
- package/dist/types/components/DDropdown/DDropdown.d.ts +2 -1
- package/dist/types/components/DLayout/DLayout.d.ts +8 -9
- package/dist/types/components/DLayout/index.d.ts +1 -0
- package/dist/types/components/DToastContainer/DToastContainer.d.ts +39 -2
- package/dist/types/components/DToastContainer/index.d.ts +2 -0
- package/dist/types/components/DToastContainer/useDToast.d.ts +38 -5
- package/dist/types/components/index.d.ts +2 -0
- package/dist/types/components/interface.d.ts +1 -1
- package/dist/types/contexts/DContext.d.ts +67 -9
- package/dist/types/contexts/DPortalContext.d.ts +66 -8
- package/dist/types/contexts/index.d.ts +1 -1
- package/dist/types/hooks/index.d.ts +0 -1
- package/dist/types/hooks/usePortal.d.ts +6 -0
- package/package.json +4 -3
- package/src/style/abstracts/_mixins.scss +46 -0
- package/src/style/abstracts/_utilities.scss +1 -1
- package/src/style/abstracts/variables/_dropdowns.scss +18 -8
- package/src/style/base/_buttons.scss +4 -0
- package/src/style/base/_dropdown.scss +5 -16
- package/src/style/base/_toast.scss +18 -16
- package/src/style/root/_root.scss +26 -0
package/dist/index.js
CHANGED
|
@@ -93,6 +93,12 @@ function useDisableBodyScrollEffect(disable) {
|
|
|
93
93
|
}, [disable]);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/**
|
|
97
|
+
* @internal
|
|
98
|
+
* Creates a DOM `<div>` portal mount point and appends it to `document.body`.
|
|
99
|
+
* This hook is used exclusively by `DPortalContextProvider` and is **not** part
|
|
100
|
+
* of the public API. Use `useDPortalContext` instead.
|
|
101
|
+
*/
|
|
96
102
|
function usePortal(portalName) {
|
|
97
103
|
const [hasPortal, setHasPortal] = React.useState(false);
|
|
98
104
|
React.useEffect(() => {
|
|
@@ -159,20 +165,22 @@ function DPortalContextProvider({ portalName, children, availablePortals, }) {
|
|
|
159
165
|
const { created } = usePortal(portalName);
|
|
160
166
|
const [stack, { push, pop, isEmpty }] = useStackState([]);
|
|
161
167
|
useDisableBodyScrollEffect(Boolean(stack.length));
|
|
162
|
-
const openPortal = React.useCallback(
|
|
168
|
+
const openPortal = React.useCallback(
|
|
169
|
+
// eslint-disable-next-line prefer-arrow-callback
|
|
170
|
+
function openPortalImpl(name, payload) {
|
|
163
171
|
var _a;
|
|
164
172
|
if (!availablePortals) {
|
|
165
|
-
throw new Error(
|
|
173
|
+
throw new Error('openPortal was called but DContextProvider has no availablePortals configured. '
|
|
174
|
+
+ 'Pass an availablePortals map to DContextProvider.');
|
|
166
175
|
}
|
|
167
176
|
const Component = availablePortals[name];
|
|
168
177
|
if (!Component) {
|
|
169
|
-
throw new Error(`
|
|
178
|
+
throw new Error(`No component registered for portal "${String(name)}". `
|
|
179
|
+
+ `Ensure "${String(name)}" has an entry in the availablePortals map on DContextProvider.`);
|
|
170
180
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
payload,
|
|
175
|
-
});
|
|
181
|
+
// K is a specific member of keyof T & string so the object satisfies
|
|
182
|
+
// InternalStackItem<T>, but TS can't verify generic-over-union assignability.
|
|
183
|
+
push({ name, Component, payload });
|
|
176
184
|
(_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.blur();
|
|
177
185
|
}, [availablePortals, push]);
|
|
178
186
|
const closePortal = React.useCallback(() => {
|
|
@@ -181,11 +189,12 @@ function DPortalContextProvider({ portalName, children, availablePortals, }) {
|
|
|
181
189
|
}
|
|
182
190
|
pop();
|
|
183
191
|
}, [isEmpty, pop]);
|
|
192
|
+
const publicStack = React.useMemo(() => stack.map(({ name, payload }) => ({ name, payload })), [stack]);
|
|
184
193
|
const value = React.useMemo(() => ({
|
|
185
|
-
stack,
|
|
194
|
+
stack: publicStack,
|
|
186
195
|
openPortal,
|
|
187
196
|
closePortal,
|
|
188
|
-
}), [
|
|
197
|
+
}), [publicStack, openPortal, closePortal]);
|
|
189
198
|
const handleClose = React.useCallback((target) => {
|
|
190
199
|
if (!(target instanceof HTMLDivElement)) {
|
|
191
200
|
return;
|
|
@@ -241,10 +250,31 @@ function DPortalContextProvider({ portalName, children, availablePortals, }) {
|
|
|
241
250
|
jsxRuntime.jsx(Component, { name: name, payload: payload }, name),
|
|
242
251
|
]) }) }), document.getElementById(portalName))] }));
|
|
243
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Hook to open/close registered portals (modals, offcanvas, etc.).
|
|
255
|
+
*
|
|
256
|
+
* **Prerequisite**: must be called inside a `DContextProvider` configured with
|
|
257
|
+
* `portalName` and `availablePortals`. `DContextProvider` mounts
|
|
258
|
+
* `DPortalContextProvider` internally — consumers never use
|
|
259
|
+
* `DPortalContextProvider` directly.
|
|
260
|
+
*
|
|
261
|
+
* @template T - Map of portal name → payload shape (e.g. `ModalPayloads`).
|
|
262
|
+
* Typing this generic gives you autocomplete on `openPortal` arguments.
|
|
263
|
+
* @returns `{ openPortal, closePortal, stack }` from the nearest portal context.
|
|
264
|
+
* @throws If called outside of `DContextProvider` / `DPortalContextProvider`.
|
|
265
|
+
*
|
|
266
|
+
* @requires DContextProvider
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* ```tsx
|
|
270
|
+
* const { openPortal, closePortal } = useDPortalContext<ModalPayloads>();
|
|
271
|
+
* openPortal('confirm', { message: 'Are you sure?' });
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
244
274
|
function useDPortalContext() {
|
|
245
275
|
const context = React.useContext(DPortalContext);
|
|
246
276
|
if (context === undefined) {
|
|
247
|
-
throw new Error('
|
|
277
|
+
throw new Error('useDPortalContext was used outside of DPortalContextProvider');
|
|
248
278
|
}
|
|
249
279
|
return context;
|
|
250
280
|
}
|
|
@@ -300,10 +330,17 @@ const DEFAULT_STATE = {
|
|
|
300
330
|
xxl: '',
|
|
301
331
|
},
|
|
302
332
|
setContext: () => { },
|
|
303
|
-
portalName: 'd-portal',
|
|
304
333
|
};
|
|
305
334
|
const DContext = React.createContext(DEFAULT_STATE);
|
|
306
|
-
|
|
335
|
+
/**
|
|
336
|
+
* Root context provider for Dynamic UI. Wrap your application with this
|
|
337
|
+
* component to configure icons, currency, language, and portal settings
|
|
338
|
+
* for all descendant Dynamic UI components.
|
|
339
|
+
*
|
|
340
|
+
* @template T - Map of portal name → payload shape (e.g. `{ modal: { title: string } }`).
|
|
341
|
+
* Pass it once at the top level: `<DContextProvider<MyPortals> ...>`.
|
|
342
|
+
*/
|
|
343
|
+
function DContextProvider({ language = DEFAULT_STATE.language, currency = DEFAULT_STATE.currency, icon = DEFAULT_STATE.icon, iconMap = DEFAULT_STATE.iconMap, portalName = 'd-portal', availablePortals, children, }) {
|
|
307
344
|
const [internalContext, setInternalContext,] = React.useState({
|
|
308
345
|
language,
|
|
309
346
|
currency,
|
|
@@ -327,12 +364,14 @@ function DContextProvider({ language = DEFAULT_STATE.language, currency = DEFAUL
|
|
|
327
364
|
const value = React.useMemo(() => (Object.assign(Object.assign({}, internalContext), { setContext })), [internalContext, setContext]);
|
|
328
365
|
return (jsxRuntime.jsx(DContext.Provider, { value: value, children: jsxRuntime.jsx(DPortalContextProvider, { portalName: portalName, availablePortals: availablePortals, children: children }) }));
|
|
329
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Returns the Dynamic UI context value set by `DContextProvider`.
|
|
369
|
+
* Falls back to the library's built-in defaults when no `DContextProvider`
|
|
370
|
+
* is present in the tree — wrap your application with `DContextProvider`
|
|
371
|
+
* to customise icons, currency, language, and portal settings.
|
|
372
|
+
*/
|
|
330
373
|
function useDContext() {
|
|
331
|
-
|
|
332
|
-
if (context === undefined) {
|
|
333
|
-
throw new Error('useDContext was used outside of DContextProvider');
|
|
334
|
-
}
|
|
335
|
-
return context;
|
|
374
|
+
return React.useContext(DContext);
|
|
336
375
|
}
|
|
337
376
|
|
|
338
377
|
function subscribeToMediaQuery(query, callback) {
|
|
@@ -2399,12 +2438,34 @@ var DToast = Object.assign(DToast$1, {
|
|
|
2399
2438
|
Body: DToastBody,
|
|
2400
2439
|
});
|
|
2401
2440
|
|
|
2402
|
-
function DToastContainer({ containerClassName, position = 'bottom-center', reverseOrder = false, containerStyle, toastOptions, gutter, }) {
|
|
2441
|
+
function DToastContainer({ containerClassName, position = 'bottom-center', reverseOrder = false, containerStyle, toastOptions, gutter = 8, }) {
|
|
2403
2442
|
return (jsxRuntime.jsx(reactHotToast.Toaster, { containerClassName: containerClassName, position: position, reverseOrder: reverseOrder, containerStyle: containerStyle, gutter: gutter, toastOptions: toastOptions }));
|
|
2404
2443
|
}
|
|
2405
2444
|
|
|
2445
|
+
/**
|
|
2446
|
+
* Hook that provides a `toast` function to dispatch DToast notifications.
|
|
2447
|
+
*
|
|
2448
|
+
* Prerequisites:
|
|
2449
|
+
* - The calling component must be inside `DContextProvider` (provides icon context).
|
|
2450
|
+
* - `DToastContainer` must be present somewhere in the component tree as a render target.
|
|
2451
|
+
*
|
|
2452
|
+
* @returns {{ toast }} Object containing a `toast` dispatcher function.
|
|
2453
|
+
* @requires DContextProvider
|
|
2454
|
+
* @requires DToastContainer
|
|
2455
|
+
* @example
|
|
2456
|
+
* const { toast } = useDToast();
|
|
2457
|
+
* toast({ title: 'Saved', color: 'success' });
|
|
2458
|
+
*/
|
|
2406
2459
|
function useDToast() {
|
|
2407
2460
|
const { iconMap: { xLg, }, } = useDContext();
|
|
2461
|
+
/**
|
|
2462
|
+
* Dispatches a toast notification rendered as a `DToast` component.
|
|
2463
|
+
* When `data` is a `ToastData` object, the default DToast layout is used.
|
|
2464
|
+
* When `data` is a render function, full custom content is rendered instead.
|
|
2465
|
+
* Returns the toast ID, which can be passed to `reactHotToast.dismiss(id)`.
|
|
2466
|
+
* @param data - Toast content as `ToastData` or a render function for custom layouts.
|
|
2467
|
+
* @param toastProps - Optional per-toast overrides: id, duration, position.
|
|
2468
|
+
*/
|
|
2408
2469
|
const toast = React.useCallback((data, toastProps) => {
|
|
2409
2470
|
if (typeof data === 'function') {
|
|
2410
2471
|
return reactHotToast.toast.custom(data, toastProps);
|
|
@@ -2571,11 +2632,14 @@ function DCreditCard({ brand = 'visa', name, number, holderText = 'Card Holder',
|
|
|
2571
2632
|
}
|
|
2572
2633
|
|
|
2573
2634
|
const getItemClass = (action) => {
|
|
2574
|
-
const base =
|
|
2575
|
-
|
|
2635
|
+
const base = classNames({
|
|
2636
|
+
'dropdown-item d-flex align-items-center': true,
|
|
2637
|
+
[`dropdown-item-${action.color}`]: !!action.color,
|
|
2638
|
+
disabled: action.disabled,
|
|
2639
|
+
});
|
|
2576
2640
|
return base;
|
|
2577
2641
|
};
|
|
2578
|
-
function DDropdown({ actions, dropdownToggle, className, }) {
|
|
2642
|
+
function DDropdown({ actions, dropdownToggle, className, classNameMenu, }) {
|
|
2579
2643
|
const [open, setOpen] = React.useState(false);
|
|
2580
2644
|
const dropdownRef = React.useRef(null);
|
|
2581
2645
|
const [position, setPosition] = React.useState('down'); // 🆕
|
|
@@ -2617,14 +2681,14 @@ function DDropdown({ actions, dropdownToggle, className, }) {
|
|
|
2617
2681
|
else {
|
|
2618
2682
|
ToggleElement = (jsxRuntime.jsx(DButtonIcon, { variant: "link", stopPropagationEnabled: false, "aria-label": "Toggle Dropdown", "aria-haspopup": "menu", "aria-expanded": open, onClick: () => setOpen(!open), icon: "MoreVertical" }));
|
|
2619
2683
|
}
|
|
2620
|
-
return (jsxRuntime.jsxs("div", { className: `dropdown
|
|
2684
|
+
return (jsxRuntime.jsxs("div", { className: classNames(`dropdown drop-${position}`, className), ref: dropdownRef, children: [ToggleElement, jsxRuntime.jsx("ul", { style: {
|
|
2621
2685
|
position: 'absolute',
|
|
2622
2686
|
top: position === 'up' ? 'auto' : '100%',
|
|
2623
2687
|
bottom: position === 'up' ? '100%' : 'auto',
|
|
2624
2688
|
left: position === 'start' ? 'auto' : 0,
|
|
2625
2689
|
right: position === 'start' ? '0' : 'auto',
|
|
2626
2690
|
transform: 'translateY(4px)',
|
|
2627
|
-
}, className:
|
|
2691
|
+
}, className: classNames('dropdown-menu p-2', { show: open }, classNameMenu), children: actions.map((action, index) => {
|
|
2628
2692
|
if (action.isDivider) {
|
|
2629
2693
|
return (jsxRuntime.jsx("hr", { className: "dropdown-divider" }, index));
|
|
2630
2694
|
}
|
|
@@ -2975,7 +3039,6 @@ exports.useMediaBreakpointUpXl = useMediaBreakpointUpXl;
|
|
|
2975
3039
|
exports.useMediaBreakpointUpXs = useMediaBreakpointUpXs;
|
|
2976
3040
|
exports.useMediaBreakpointUpXxl = useMediaBreakpointUpXxl;
|
|
2977
3041
|
exports.useMediaQuery = useMediaQuery;
|
|
2978
|
-
exports.usePortal = usePortal;
|
|
2979
3042
|
exports.useProvidedRefOrCreate = useProvidedRefOrCreate;
|
|
2980
3043
|
exports.useStackState = useStackState;
|
|
2981
3044
|
exports.useTabContext = useTabContext;
|