@linzjs/lui 21.35.0 → 21.36.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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [21.36.0](https://github.com/linz/lui/compare/v21.35.0...v21.36.0) (2024-07-01)
2
+
3
+
4
+ ### Features
5
+
6
+ * **useToast:** merge update & add APIs ([#1128](https://github.com/linz/lui/issues/1128)) ([1cdd7b6](https://github.com/linz/lui/commit/1cdd7b6c9c412d4cbc8d76e042328d4f3242133a))
7
+
1
8
  # [21.35.0](https://github.com/linz/lui/compare/v21.34.0...v21.35.0) (2024-06-26)
2
9
 
3
10
 
@@ -18,11 +18,6 @@ export declare type ToastAction = {
18
18
  } | {
19
19
  type: 'remove';
20
20
  id: number;
21
- } | {
22
- type: 'update';
23
- notification: ToastNode;
24
- options?: ToastOptions;
25
- id: number;
26
21
  };
27
22
  export interface IToast {
28
23
  id: number;
@@ -0,0 +1,3 @@
1
+ /// <reference types="react" />
2
+ export declare const ToastVersion: import("react").Context<"v1" | "v2">;
3
+ export declare const useToastVersion: () => "v1" | "v2";
@@ -1,13 +1,38 @@
1
1
  import { ComponentProps, ReactNode } from 'react';
2
2
  import { LuiToastMessage as LuiToastMessageOld } from '../../LuiToastMessage/LuiToastMessage';
3
- declare type ProviderProps = {
4
- children: ReactNode;
5
- upgrade?: boolean;
3
+ declare type ProviderPropsV1 = {
4
+ /** Use v2 explicitely to get the new designs */
5
+ version?: 'v1';
6
+ /** Default timeout is not permitted in v1 */
7
+ defaultTimeout?: never;
8
+ /** Stack is not permitted in v1 */
9
+ stack?: never;
10
+ };
11
+ declare type ProviderPropsV2 = {
12
+ /** Latest designs */
13
+ version: 'v2';
14
+ /** Default timeout for ephemeral toasts is 4000ms */
15
+ defaultTimeout?: number;
16
+ /** Max number of toast allowed simultaneously. Default value is 5. */
17
+ stack?: number;
6
18
  };
19
+ declare type ProviderProps = {
20
+ children?: ReactNode;
21
+ } & (ProviderPropsV1 | ProviderPropsV2);
7
22
  /**
8
- * @deprecated Use ToastProvider or LuiMessagingContextProvider with upgrade prop set to true to get the new designs
23
+ * Context provider to handle global logic of toast messages. It defaults to legacy version 'v1'.
24
+ * @description Set version property to 'v2' to get the latest designs.
9
25
  */
10
26
  export declare const LuiMessagingContextProvider: (props: ProviderProps) => JSX.Element;
27
+ /**
28
+ * Hook to display pop-up messages in response to a user action or state change. Examples include: Saving, exporting, committing, deleting, etc.
29
+ * @description Legacy hook to trigger toasts. It requires {@linkcode LuiMessagingContextProvider}.
30
+ * @alias useToast. For a more flexible API when you're using v2 designs, using useToast is recommended.
31
+ * @returns Toaster function to trigger branded toasts (e.g. warning, error, info) in the top right of the page.
32
+ * @example
33
+ * const toaster = useShowLUIMessage();
34
+ * toaster({ message: 'Failed to save', messageType: 'toast', messageLevel: 'error' });
35
+ */
11
36
  export declare const useShowLUIMessage: () => (props: import("../../../contexts/LuiMessagingContextProvider").ShowMessageProps) => void;
12
37
  declare type LuiToastMessageProps = ComponentProps<typeof LuiToastMessageOld>;
13
38
  export declare const LuiToastMessage: (props: LuiToastMessageProps) => JSX.Element;
@@ -2,4 +2,4 @@ import { ShowMessageProps } from '../../../contexts/LuiMessagingContextProvider'
2
2
  /**
3
3
  * Provide new toast hook with old interface from original useShowLUIMessage
4
4
  */
5
- export declare const useShowLUIMessageCompatibleInterface: () => (props: ShowMessageProps) => void;
5
+ export declare const useShowLUIMessageCompatibleInterface: () => (props: ShowMessageProps) => number;
@@ -1,10 +1,38 @@
1
1
  import './ToastProvider.scss';
2
2
  import { ReactNode } from 'react';
3
3
  import { ToastNode, ToastOptions } from './Helpers/ToastTypes';
4
- declare type BannerToast = (children: ReactNode, config?: ToastOptions) => number;
5
- declare type Result = {
6
- toast: (notification: ToastNode, options?: ToastOptions) => number;
7
- update: (toastId: number, notification: ToastNode, options?: ToastOptions) => number;
4
+ declare type Config = ToastOptions & {
5
+ id?: number;
6
+ };
7
+ declare type BannerToast = (children: ReactNode, config?: Config) => number;
8
+ /**
9
+ * Hook to display pop-up messages in response to a user action or state change. Examples include: Saving, exporting, committing, deleting, etc.
10
+ * It requires 'LuiMessagingContextProvider' with property version set to 'v2'.
11
+ * Toasts that are `warning` or `error` won't timeout automatically, unless configured otherwise.
12
+ * @description Hook to trigger pop-up messages (a.k.a. toasts). Each toast helper function returns a a toast id `number` that can be used for manual dismiss or updates.
13
+ * @example
14
+ * // Error message
15
+ * const { error } = useToast();
16
+ * error("Failed to save");
17
+ * @example
18
+ * // Toast error and then turn toast into success
19
+ * const { error, success } = useToast();
20
+ * const toastId = error("Failed to save");
21
+ * success('Toast updated to success', { id: toastId });
22
+ * @example
23
+ * // Dismiss info toast that won't dismiss automatically
24
+ * const { info, dismiss } = useToast();
25
+ * const toastId = info(<a href="#">Some useful link</a>, { timeout: Infinity });
26
+ * dismiss(toastId);
27
+ * @example
28
+ * // Custom toasts accept `ReactNode` or `({ close: () => void }) => ReactNode`
29
+ * const { toast } = useToast();
30
+ * toast(({ close }) => <button onClick={() => close()}>Click me to dismiss</button>, { timeout: Infinity });
31
+ * toast(<div>I will be dismissed in 3s.</div>, { timeout: 3000 });
32
+ * toast("I'm placed in the topLeft of the page", { position: 'topLeft' });
33
+ */
34
+ export declare const useToast: () => {
35
+ toast: (notification: ToastNode, config?: Config) => number;
8
36
  dismiss: (toastId: number) => void;
9
37
  info: BannerToast;
10
38
  success: BannerToast;
@@ -12,5 +40,4 @@ declare type Result = {
12
40
  warning: BannerToast;
13
41
  plain: BannerToast;
14
42
  };
15
- export declare const useToast: () => Result;
16
43
  export {};
package/dist/index.d.ts CHANGED
@@ -72,5 +72,5 @@ export { LuiPagination } from './components/LuiPagination/LuiPagination';
72
72
  export { Splitter } from './components/Splitter/Splitter';
73
73
  export { useSplitterRef } from './components/Splitter/useSplitterRef';
74
74
  export { LuiBannerV2 } from './components/LuiBannerV2/LuiBannerV2';
75
- export { useToast, ToastProvider } from './components/Toast';
75
+ export { useToast } from './components/Toast';
76
76
  export { LuiMessagingContextProvider, useShowLUIMessage, LuiToastMessage, } from './components/Toast/Upgrade';
package/dist/index.js CHANGED
@@ -59410,20 +59410,23 @@ var Toast = function (props) {
59410
59410
  var reducer = function (state, action) {
59411
59411
  var updatedAt = new Date().getTime();
59412
59412
  if (action.type === 'add') {
59413
- var createdAt = updatedAt;
59414
- var notification = action.notification, options = action.options, id = action.id;
59415
- var toast = { notification: notification, options: options, id: id, updatedAt: updatedAt, createdAt: createdAt };
59416
- var toasts = __spreadArray([toast], state.toasts, true);
59417
- return trim(__assign(__assign({}, state), { toasts: toasts }));
59413
+ var notification = action.notification, options = action.options, id_1 = action.id;
59414
+ var base = { notification: notification, options: options, id: id_1, updatedAt: updatedAt };
59415
+ var found = state.toasts.find(function (t) { return t.id === id_1; });
59416
+ if (!found) {
59417
+ var toast = __assign(__assign({}, base), { createdAt: updatedAt });
59418
+ var toasts = __spreadArray([toast], state.toasts, true);
59419
+ return trim(__assign(__assign({}, state), { toasts: toasts }));
59420
+ }
59421
+ else {
59422
+ var toast_1 = __assign(__assign({}, found), base);
59423
+ var toasts = state.toasts.map(function (t) { return (t.id !== id_1 ? t : toast_1); });
59424
+ return __assign(__assign({}, state), { toasts: toasts });
59425
+ }
59418
59426
  }
59419
59427
  if (action.type === 'remove') {
59420
59428
  return __assign(__assign({}, state), { toasts: state.toasts.filter(function (t) { return t.id !== action.id; }) });
59421
59429
  }
59422
- if (action.type === 'update') {
59423
- var notification = action.notification, options = action.options, id_1 = action.id;
59424
- var updated_1 = { notification: notification, options: options, id: id_1, updatedAt: updatedAt };
59425
- return __assign(__assign({}, state), { toasts: state.toasts.map(function (t) { return (t.id !== id_1 ? t : __assign(__assign({}, t), updated_1)); }) });
59426
- }
59427
59430
  return state;
59428
59431
  };
59429
59432
  var useToastState = function (_a) {
@@ -59459,6 +59462,9 @@ var trim = function (state) { return (__assign(__assign({}, state), { toasts: Ob
59459
59462
  })
59460
59463
  .flat() })); };
59461
59464
 
59465
+ var ToastVersion = React.createContext('v1');
59466
+ var useToastVersion = function () { return React.useContext(ToastVersion); };
59467
+
59462
59468
  var ToastContext = React__namespace.createContext(undefined);
59463
59469
  var portal = getToastProviderEl();
59464
59470
  var ToastProvider = function (_a) {
@@ -59468,7 +59474,7 @@ var ToastProvider = function (_a) {
59468
59474
  if (React.useContext(ToastContext)) {
59469
59475
  return React__namespace.createElement(React__namespace.Fragment, null, children);
59470
59476
  }
59471
- return (React__namespace.createElement(React__namespace.Fragment, null,
59477
+ return (React__namespace.createElement(ToastVersion.Provider, { value: "v2" },
59472
59478
  ReactDOM__default["default"].createPortal(React__namespace.createElement(React__namespace.Fragment, null, Object.entries(toasts).map(function (_a) {
59473
59479
  var section = _a[0], list = _a[1];
59474
59480
  return (React__namespace.createElement(ToastSection, __assign({ key: section }, {
@@ -59481,30 +59487,35 @@ var ToastProvider = function (_a) {
59481
59487
  React__namespace.createElement(ToastContext.Provider, { value: dispatch }, children)));
59482
59488
  };
59483
59489
 
59484
- var getUniqueToastId = function () { return Math.floor(Math.random() * 10000); };
59490
+ var getUniqueToastId = function () {
59491
+ var _a;
59492
+ var id = Number("".concat(Math.floor(Math.random() * 10000000)).slice(0, 4));
59493
+ var selector = "[data-toastid=\"".concat(id, "\"]");
59494
+ var duplicate = ((_a = getToastProviderEl().querySelectorAll(selector)) === null || _a === void 0 ? void 0 : _a.length) > 0;
59495
+ if (duplicate) {
59496
+ return getUniqueToastId();
59497
+ }
59498
+ return id;
59499
+ };
59485
59500
  var toastFunctions = function (dispatch) {
59486
- var toast = function (notification, options) {
59487
- var id = getUniqueToastId();
59501
+ var toast = function (notification, config) {
59502
+ if (config === void 0) { config = {}; }
59503
+ var _a = config.id, id = _a === void 0 ? getUniqueToastId() : _a, options = __rest(config, ["id"]);
59488
59504
  dispatch === null || dispatch === void 0 ? void 0 : dispatch({ type: 'add', notification: notification, options: options, id: id });
59489
59505
  return id;
59490
59506
  };
59491
- var update = function (id, notification, options) {
59492
- dispatch === null || dispatch === void 0 ? void 0 : dispatch({ type: 'update', notification: notification, options: options, id: id });
59493
- return id;
59494
- };
59495
59507
  var banner = function (level, defaults) {
59496
59508
  if (defaults === void 0) { defaults = {}; }
59497
59509
  return function (children, options) {
59498
59510
  if (options === void 0) { options = defaults; }
59499
59511
  return toast(function (_a) {
59500
- var close = _a.close;
59501
- return (React__namespace.createElement(LuiBannerV2, __assign({}, { level: level, children: children }, { onDismiss: options.timeout === Infinity ? close : undefined })));
59512
+ var onDismiss = _a.close;
59513
+ return (React__namespace.createElement(LuiBannerV2, __assign({}, { level: level, children: children, onDismiss: onDismiss })));
59502
59514
  }, options);
59503
59515
  };
59504
59516
  };
59505
59517
  return {
59506
59518
  toast: toast,
59507
- update: update,
59508
59519
  dismiss: function (id) { return dispatch === null || dispatch === void 0 ? void 0 : dispatch({ type: 'remove', id: id }); },
59509
59520
  info: banner('info'),
59510
59521
  success: banner('success'),
@@ -59513,6 +59524,32 @@ var toastFunctions = function (dispatch) {
59513
59524
  plain: banner()
59514
59525
  };
59515
59526
  };
59527
+ /**
59528
+ * Hook to display pop-up messages in response to a user action or state change. Examples include: Saving, exporting, committing, deleting, etc.
59529
+ * It requires 'LuiMessagingContextProvider' with property version set to 'v2'.
59530
+ * Toasts that are `warning` or `error` won't timeout automatically, unless configured otherwise.
59531
+ * @description Hook to trigger pop-up messages (a.k.a. toasts). Each toast helper function returns a a toast id `number` that can be used for manual dismiss or updates.
59532
+ * @example
59533
+ * // Error message
59534
+ * const { error } = useToast();
59535
+ * error("Failed to save");
59536
+ * @example
59537
+ * // Toast error and then turn toast into success
59538
+ * const { error, success } = useToast();
59539
+ * const toastId = error("Failed to save");
59540
+ * success('Toast updated to success', { id: toastId });
59541
+ * @example
59542
+ * // Dismiss info toast that won't dismiss automatically
59543
+ * const { info, dismiss } = useToast();
59544
+ * const toastId = info(<a href="#">Some useful link</a>, { timeout: Infinity });
59545
+ * dismiss(toastId);
59546
+ * @example
59547
+ * // Custom toasts accept `ReactNode` or `({ close: () => void }) => ReactNode`
59548
+ * const { toast } = useToast();
59549
+ * toast(({ close }) => <button onClick={() => close()}>Click me to dismiss</button>, { timeout: Infinity });
59550
+ * toast(<div>I will be dismissed in 3s.</div>, { timeout: 3000 });
59551
+ * toast("I'm placed in the topLeft of the page", { position: 'topLeft' });
59552
+ */
59516
59553
  var useToast = function () {
59517
59554
  var dispatch = React.useContext(ToastContext);
59518
59555
  return React.useMemo(function () { return toastFunctions(dispatch); }, [dispatch]);
@@ -59604,7 +59641,7 @@ var LuiToastMessageCompatibleInterface = function (props) {
59604
59641
  var toastIdRef = React.useRef(undefined);
59605
59642
  var onceDismissedRef = React.useRef(false);
59606
59643
  var displayRef = React.useRef(display);
59607
- var _c = useToast(), toast = _c.toast, dismiss = _c.dismiss, update = _c.update;
59644
+ var _c = useToast(), toast = _c.toast, dismiss = _c.dismiss;
59608
59645
  displayRef.current = display;
59609
59646
  // Close when it's unmounted
59610
59647
  React.useEffect(function () {
@@ -59620,23 +59657,18 @@ var LuiToastMessageCompatibleInterface = function (props) {
59620
59657
  if (display && !onceDismissed) {
59621
59658
  var notification = function (_a) {
59622
59659
  var close = _a.close;
59623
- var onDismiss = requireDismiss ? close : undefined;
59624
- return React__default["default"].createElement(LuiBannerV2, __assign({}, { level: level, onDismiss: onDismiss, children: children }));
59660
+ return React__default["default"].createElement(LuiBannerV2, __assign({}, { level: level, onDismiss: close, children: children }));
59625
59661
  };
59626
59662
  var options = {
59627
- timeout: displayTimeout === 0 ? Infinity : displayTimeout,
59663
+ timeout: requireDismiss || !displayTimeout ? Infinity : displayTimeout,
59628
59664
  onLeave: function () {
59629
59665
  onClose === null || onClose === void 0 ? void 0 : onClose();
59630
59666
  onceDismissedRef.current = displayRef.current;
59631
59667
  toastIdRef.current = undefined;
59632
- }
59668
+ },
59669
+ id: toastIdRef.current
59633
59670
  };
59634
- if (!toastIdRef.current) {
59635
- toastIdRef.current = toast(notification, options);
59636
- }
59637
- else {
59638
- update(toastIdRef.current, notification, options);
59639
- }
59671
+ toastIdRef.current = toast(notification, options);
59640
59672
  }
59641
59673
  if (!display && toastIdRef.current !== undefined) {
59642
59674
  dismiss(toastIdRef.current);
@@ -59646,47 +59678,49 @@ var LuiToastMessageCompatibleInterface = function (props) {
59646
59678
  return React__default["default"].createElement(React__default["default"].Fragment, null);
59647
59679
  };
59648
59680
 
59681
+ var getTimeout = function (_a) {
59682
+ var requireDismiss = _a.requireDismiss, messageLevel = _a.messageLevel;
59683
+ return (requireDismiss !== null && requireDismiss !== void 0 ? requireDismiss : messageLevel === 'error') ? Infinity : undefined;
59684
+ };
59649
59685
  /**
59650
59686
  * Provide new toast hook with old interface from original useShowLUIMessage
59651
59687
  */
59652
59688
  var useShowLUIMessageCompatibleInterface = function () {
59653
- var toast = useToast().toast;
59689
+ var t = useToast();
59654
59690
  return function (props) {
59655
- var message = props.message, messageLevel = props.messageLevel, requireDismiss = props.requireDismiss;
59656
- var toastProps = { children: message, level: messageLevel };
59657
- if (requireDismiss || messageLevel === 'error') {
59658
- toast(function (_a) {
59659
- var close = _a.close;
59660
- return React__default["default"].createElement(LuiBannerV2, __assign({}, toastProps, { onDismiss: close }));
59661
- });
59662
- }
59663
- else {
59664
- toast(React__default["default"].createElement(LuiBannerV2, __assign({}, toastProps)));
59665
- }
59691
+ return t[props.messageLevel](props.message, { timeout: getTimeout(props) });
59666
59692
  };
59667
59693
  };
59668
59694
 
59669
- var ToastUpgrade = React.createContext(false);
59670
- var useToastUpgrade = function () { return React.useContext(ToastUpgrade); };
59671
59695
  /**
59672
- * @deprecated Use ToastProvider or LuiMessagingContextProvider with upgrade prop set to true to get the new designs
59696
+ * Context provider to handle global logic of toast messages. It defaults to legacy version 'v1'.
59697
+ * @description Set version property to 'v2' to get the latest designs.
59673
59698
  */
59674
59699
  var LuiMessagingContextProvider = function (props) {
59675
- var _a = props.upgrade, upgrade = _a === void 0 ? false : _a, rest = __rest(props, ["upgrade"]);
59676
59700
  if (React.useContext(ToastContext)) {
59677
- return React__default["default"].createElement(React__default["default"].Fragment, null, rest.children);
59701
+ return React__default["default"].createElement(React__default["default"].Fragment, null, props.children);
59678
59702
  }
59679
- return (React__default["default"].createElement(ToastUpgrade.Provider, { value: upgrade }, upgrade ? (React__default["default"].createElement(ToastProvider, __assign({}, rest))) : (React__default["default"].createElement(LuiMessagingContextProvider$1, __assign({}, rest)))));
59703
+ return (React__default["default"].createElement(React__default["default"].Fragment, null, props.version === 'v2' ? (React__default["default"].createElement(ToastProvider, { stack: props.stack, defaultTimeout: props.defaultTimeout }, props.children)) : (React__default["default"].createElement(ToastVersion.Provider, { value: "v1" },
59704
+ React__default["default"].createElement(LuiMessagingContextProvider$1, null, props.children)))));
59680
59705
  };
59706
+ /**
59707
+ * Hook to display pop-up messages in response to a user action or state change. Examples include: Saving, exporting, committing, deleting, etc.
59708
+ * @description Legacy hook to trigger toasts. It requires {@linkcode LuiMessagingContextProvider}.
59709
+ * @alias useToast. For a more flexible API when you're using v2 designs, using useToast is recommended.
59710
+ * @returns Toaster function to trigger branded toasts (e.g. warning, error, info) in the top right of the page.
59711
+ * @example
59712
+ * const toaster = useShowLUIMessage();
59713
+ * toaster({ message: 'Failed to save', messageType: 'toast', messageLevel: 'error' });
59714
+ */
59681
59715
  var useShowLUIMessage = function () {
59682
- var upgrade = useToastUpgrade();
59716
+ var version = useToastVersion();
59683
59717
  var older = useShowLUIMessage$1();
59684
59718
  var newer = useShowLUIMessageCompatibleInterface();
59685
- return upgrade ? newer : older;
59719
+ return version === 'v2' ? newer : older;
59686
59720
  };
59687
59721
  var LuiToastMessage = function (props) {
59688
- var upgrade = useToastUpgrade();
59689
- return upgrade ? (React__default["default"].createElement(LuiToastMessageCompatibleInterface, __assign({}, props))) : (React__default["default"].createElement(LuiToastMessage$1, __assign({}, props)));
59722
+ var version = useToastVersion();
59723
+ return version === 'v2' ? (React__default["default"].createElement(LuiToastMessageCompatibleInterface, __assign({}, props))) : (React__default["default"].createElement(LuiToastMessage$1, __assign({}, props)));
59690
59724
  };
59691
59725
 
59692
59726
  exports.CheckboxItemRenderer = CheckboxItemRenderer;
@@ -59788,7 +59822,6 @@ exports.LuiTooltip = LuiTooltip;
59788
59822
  exports.LuiUpdatesSplashModal = LuiUpdatesSplashModal;
59789
59823
  exports.RadioItemRenderer = RadioItemRenderer;
59790
59824
  exports.Splitter = Splitter;
59791
- exports.ToastProvider = ToastProvider;
59792
59825
  exports.ToolbarButton = ToolbarButton;
59793
59826
  exports.ToolbarItem = ToolbarItem;
59794
59827
  exports.ToolbarItemSeparator = ToolbarItemSeparator;