@xh/hoist 75.0.0-SNAPSHOT.1753490062755 → 75.0.0-SNAPSHOT.1753722797428

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
@@ -21,6 +21,8 @@
21
21
  * Deprecated the `RelativeTimestamp.options` prop - all the same options are now top-level props.
22
22
  * Added new `GroupingChooserModel.sortDimensions` config. Set to `false` to respect the order in
23
23
  which `dimensions` are provided to the model.
24
+ * Added new `ClipboardButton.errorMessage` prop to customize or suppress a toast alert if the copy
25
+ operation fails. Set to `false` to fail silently (the behavior prior to this change).
24
26
 
25
27
  ### 🐞 Bug Fixes
26
28
 
@@ -1,85 +1,96 @@
1
1
  import { HoistAppModel, HoistAuthModel, ElementFactory, HoistProps } from '@xh/hoist/core';
2
2
  import { Component, ComponentClass, FunctionComponent } from 'react';
3
3
  /**
4
- * Specification for a client-side Hoist application.
5
- * Passed to XH.renderApp() to kick-off app rendering and available thereafter as `XH.appSpec`.
4
+ * Spec for a client-side Hoist application. A config matching this class's shape is provided
5
+ * to {@link XHApi.renderApp} to kick-off app rendering and is available thereafter as `XH.appSpec`.
6
6
  */
7
7
  export declare class AppSpec<T extends HoistAppModel = HoistAppModel> {
8
+ /**
9
+ * AuthModel class for the application. Note this is a reference to the class itself, not an
10
+ * instance, and must extend {@link HoistAuthModel}. If not provided, Hoist will fallback to
11
+ * the superclass implementation, but note that class would only be suitable for fully
12
+ * transparent SSO based solutions such as NTLM. Common patterns such as OAuth will require an
13
+ * extended implementation.
14
+ */
15
+ authModelClass?: new () => HoistAuthModel;
16
+ /**
17
+ * Method for determining if user may access the app. If a string, will be interpreted as the
18
+ * role required for basic UI access. Return false to show a generated lockout message, or use
19
+ * the object form to provide a custom message.
20
+ */
21
+ checkAccess: string | ((user: object) => boolean | {
22
+ hasAccess: boolean;
23
+ message: string;
24
+ });
8
25
  /**
9
26
  * Short code for this particular JS client application.
27
+ *
10
28
  * Will default to the `appCode` specified within the project's Webpack config, but can be
11
29
  * set to a more specific value (e.g. 'myAppMobile') to identify the client app in common
12
- * code or configs.
30
+ * code or configs that support distinct settings for different client apps.
13
31
  */
14
32
  clientAppCode?: string;
15
33
  /**
16
34
  * Display name for this particular JS client application.
35
+ *
17
36
  * As with `clientAppCode` above, this will default to the global `appName` specified by
18
37
  * the project's Webpack config, but can be set here to a more specific value (e.g.
19
38
  * 'MyApp Mobile').
20
39
  */
21
40
  clientAppName?: string;
22
41
  /**
23
- * Root Model class for the application. Note this is a reference to the class itself, not an
24
- * instance, and must extend {@link HoistAppModel}.
25
- */
26
- modelClass: new () => T;
27
- /**
28
- * AuthModel class for the application. Note this is a reference to the class itself, not an
29
- * instance, and must extend {@link HoistAuthModel}.
30
- */
31
- authModelClass?: new () => HoistAuthModel;
32
- /**
33
- * Root HoistComponent for the application. Despite the name, functional components are fully
34
- * supported and expected.
42
+ * Root HoistComponent for the application.
43
+ * Despite the name, functional components are fully supported and expected.
35
44
  */
36
45
  componentClass: ComponentClass<HoistProps> | FunctionComponent<HoistProps>;
37
46
  /**
38
47
  * Container component to be used to host this application.
39
- * This class determines the platform used by Hoist. The value should be imported from
48
+ *
49
+ * This class determines the platform used by Hoist - almost all apps will import and specify
40
50
  * either `@xh/hoist/desktop/AppContainer` or `@xh/hoist/mobile/AppContainer`.
41
51
  */
42
52
  containerClass: ComponentClass<HoistProps> | FunctionComponent<HoistProps>;
43
- /** True if the app should use the Hoist mobile toolkit.*/
44
- isMobileApp: boolean;
45
- /** True to show a login form on initialization when not authenticated. (default false) */
46
- enableLoginForm?: boolean;
47
53
  /**
48
- * True to show logout options in the app, for apps with auth schemes that can support this
49
- * operation (e.g. OAuth). (default false)
54
+ * True to disable Field-level XSS protection by default across all Stores/Fields in the app.
55
+ * For use with secure, internal apps that do not display arbitrary/external user input and
56
+ * have tight performance tolerances and/or load very large record sets.
57
+ * @see FieldSpec.disableXssProtection
50
58
  */
51
- enableLogout?: boolean;
59
+ disableXssProtection?: boolean;
52
60
  /**
53
- * Method for determining if user may access the app.
54
- * If a string, will be interpreted as the role required for basic UI access.
61
+ * True to show a login form on initialization when not authenticated. Default is `false` as
62
+ * most Hoist applications are expected to use OAuth or SSO for authn.
55
63
  */
56
- checkAccess: string | ((user: object) => boolean | {
57
- hasAccess: boolean;
58
- message: string;
59
- });
64
+ enableLoginForm?: boolean;
60
65
  /**
61
- * Write a track log statement after the app has loaded and fully initialized?
62
- * Message will include elapsed time of asset loading and init.
66
+ * True to show logout options in the app, for apps with auth schemes that can support this
67
+ * operation (e.g. OAuth). Default is `false` as most Hoist applications are expected to use
68
+ * SSO or to run in internal environments where logout is not required / typical.
63
69
  */
64
- trackAppLoad?: boolean;
65
- /** True to enable Hoist websocket connectivity. (default false) */
66
- webSocketsEnabled?: boolean;
70
+ enableLogout?: boolean;
67
71
  /**
68
- * Optional custom Component to display when App has been suspended. The component will
69
- * receive a single prop -- onReactivate -- a callback called when the user has acknowledged
72
+ * Optional custom Component to display when the app has been suspended. The component will
73
+ * receive a single `onReactivate` prop, a no-arg callback fired when the user has acknowledged
70
74
  * the suspension and wishes to reload the app.
71
75
  */
72
76
  idlePanel?: ElementFactory | FunctionComponent | Component;
77
+ /** True if the app should use the Hoist mobile toolkit.*/
78
+ isMobileApp: boolean;
73
79
  /**
74
80
  * Optional custom Component to display when the user is denied access to app. Intended for
75
81
  * apps that implement custom auth flows. See also `lockoutMessage` for a more lightweight
76
82
  * customization option.
77
83
  */
78
84
  lockoutPanel?: ElementFactory | FunctionComponent | Component;
79
- /** Optional message to show on login form (see showLoginForm). */
80
- loginMessage?: string;
81
85
  /** Optional message to show users when denied access to app. */
82
86
  lockoutMessage?: string;
87
+ /** Optional message to show on login form, if `showLoginForm: true`. */
88
+ loginMessage?: string;
89
+ /**
90
+ * Root Model class for the application. Note this is a reference to the class itself, not an
91
+ * instance, and must extend {@link HoistAppModel}.
92
+ */
93
+ modelClass: new () => T;
83
94
  /**
84
95
  * True to show the built-in browser context menu when no app-specific menu would be shown
85
96
  * (e.g. from a Grid). False (the default) prevents the browser menu from being shown anywhere
@@ -87,30 +98,30 @@ export declare class AppSpec<T extends HoistAppModel = HoistAppModel> {
87
98
  */
88
99
  showBrowserContextMenu?: boolean;
89
100
  /**
90
- * True to disable Field-level XSS protection by default across all Stores/Fields in the app.
91
- * For use with secure, internal apps that do not display arbitrary/external user input and
92
- * have tight performance tolerances and/or load very large record sets.
93
- * @see FieldConfig.disableXssProtection
101
+ * True (default) to write a track log statement after the app has loaded and fully
102
+ * initialized, including a breakdown of elapsed time throughout the init process.
94
103
  */
95
- disableXssProtection?: boolean;
96
- constructor({ clientAppCode, clientAppName, modelClass, componentClass, containerClass, authModelClass, isMobileApp, checkAccess, enableLoginForm, enableLogout, trackAppLoad, webSocketsEnabled, idlePanel, lockoutPanel, loginMessage, lockoutMessage, showBrowserContextMenu, disableXssProtection }: {
104
+ trackAppLoad?: boolean;
105
+ /** True to enable Hoist websocket connectivity. */
106
+ webSocketsEnabled?: boolean;
107
+ constructor({ authModelClass, checkAccess, clientAppCode, clientAppName, componentClass, containerClass, disableXssProtection, enableLoginForm, enableLogout, idlePanel, isMobileApp, lockoutMessage, lockoutPanel, loginMessage, modelClass, showBrowserContextMenu, trackAppLoad, webSocketsEnabled }: {
108
+ authModelClass?: typeof HoistAuthModel;
109
+ checkAccess: any;
97
110
  clientAppCode?: string;
98
111
  clientAppName?: string;
99
- modelClass: any;
100
112
  componentClass: any;
101
113
  containerClass: any;
102
- authModelClass?: typeof HoistAuthModel;
103
- isMobileApp: any;
104
- checkAccess: any;
114
+ disableXssProtection?: boolean;
105
115
  enableLoginForm?: boolean;
106
116
  enableLogout?: boolean;
107
- trackAppLoad?: boolean;
108
- webSocketsEnabled?: boolean;
109
117
  idlePanel?: any;
118
+ isMobileApp: any;
119
+ lockoutMessage?: any;
110
120
  lockoutPanel?: any;
111
121
  loginMessage?: any;
112
- lockoutMessage?: any;
122
+ modelClass: any;
113
123
  showBrowserContextMenu?: boolean;
114
- disableXssProtection?: boolean;
124
+ trackAppLoad?: boolean;
125
+ webSocketsEnabled?: boolean;
115
126
  });
116
127
  }
@@ -2,13 +2,13 @@ import { HoistModel, PlainObject } from './';
2
2
  /**
3
3
  * Base class for managing authentication lifecycle.
4
4
  *
5
- * Hoist will consult this model early in the initialization sequence, prior to full Hoist
5
+ * Hoist will consult this model early in the initialization sequence, prior to full Hoist
6
6
  * initialization. This means that several core services (identity, configs, prefs) will *not* be
7
7
  * available, but it provides the app a hook to do early service initialization or other work to
8
8
  * support flows such as OAuth.
9
9
  *
10
10
  * The base implementation of this class is minimal and would be adequate only for fully
11
- * transparent SSO based solutions such as NTLM. Applications wishing to provide custom
11
+ * transparent SSO based solutions such as NTLM. Applications wishing to provide custom
12
12
  * authentication should spec an extended version of this class in the {@link AppSpec} passed to
13
13
  * {@link XHApi#renderApp}.
14
14
  */
@@ -3,8 +3,16 @@ import '@xh/hoist/desktop/register';
3
3
  export interface ClipboardButtonProps extends ButtonProps {
4
4
  /** Function returning the text to copy. May be async. */
5
5
  getCopyText: () => string | Promise<string>;
6
- /** Message to be displayed in a toast when copy is complete. */
7
- successMessage?: string;
6
+ /**
7
+ * Message to be displayed in a toast should the copy operation fail, or `true` (default) to
8
+ * show a toast-based alert from `XH.handleException`. Spec `false` to fail silently.
9
+ */
10
+ errorMessage?: string | boolean;
11
+ /**
12
+ * Message to be displayed in a toast when copy is complete, or `true` for a default success
13
+ * confirmation. Default `false`
14
+ */
15
+ successMessage?: string | boolean;
8
16
  }
9
17
  /**
10
18
  * Button to copy text to the clipboard.
package/core/AppSpec.ts CHANGED
@@ -10,20 +10,38 @@ import {isFunction, isNil, isString} from 'lodash';
10
10
  import {Component, ComponentClass, FunctionComponent} from 'react';
11
11
 
12
12
  /**
13
- * Specification for a client-side Hoist application.
14
- * Passed to XH.renderApp() to kick-off app rendering and available thereafter as `XH.appSpec`.
13
+ * Spec for a client-side Hoist application. A config matching this class's shape is provided
14
+ * to {@link XHApi.renderApp} to kick-off app rendering and is available thereafter as `XH.appSpec`.
15
15
  */
16
16
  export class AppSpec<T extends HoistAppModel = HoistAppModel> {
17
+ /**
18
+ * AuthModel class for the application. Note this is a reference to the class itself, not an
19
+ * instance, and must extend {@link HoistAuthModel}. If not provided, Hoist will fallback to
20
+ * the superclass implementation, but note that class would only be suitable for fully
21
+ * transparent SSO based solutions such as NTLM. Common patterns such as OAuth will require an
22
+ * extended implementation.
23
+ */
24
+ authModelClass?: new () => HoistAuthModel;
25
+
26
+ /**
27
+ * Method for determining if user may access the app. If a string, will be interpreted as the
28
+ * role required for basic UI access. Return false to show a generated lockout message, or use
29
+ * the object form to provide a custom message.
30
+ */
31
+ checkAccess: string | ((user: object) => boolean | {hasAccess: boolean; message: string});
32
+
17
33
  /**
18
34
  * Short code for this particular JS client application.
35
+ *
19
36
  * Will default to the `appCode` specified within the project's Webpack config, but can be
20
37
  * set to a more specific value (e.g. 'myAppMobile') to identify the client app in common
21
- * code or configs.
38
+ * code or configs that support distinct settings for different client apps.
22
39
  */
23
40
  clientAppCode?: string;
24
41
 
25
42
  /**
26
43
  * Display name for this particular JS client application.
44
+ *
27
45
  * As with `clientAppCode` above, this will default to the global `appName` specified by
28
46
  * the project's Webpack config, but can be set here to a more specific value (e.g.
29
47
  * 'MyApp Mobile').
@@ -31,64 +49,50 @@ export class AppSpec<T extends HoistAppModel = HoistAppModel> {
31
49
  clientAppName?: string;
32
50
 
33
51
  /**
34
- * Root Model class for the application. Note this is a reference to the class itself, not an
35
- * instance, and must extend {@link HoistAppModel}.
36
- */
37
- modelClass: new () => T;
38
-
39
- /**
40
- * AuthModel class for the application. Note this is a reference to the class itself, not an
41
- * instance, and must extend {@link HoistAuthModel}.
42
- */
43
- authModelClass?: new () => HoistAuthModel;
44
-
45
- /**
46
- * Root HoistComponent for the application. Despite the name, functional components are fully
47
- * supported and expected.
52
+ * Root HoistComponent for the application.
53
+ * Despite the name, functional components are fully supported and expected.
48
54
  */
49
55
  componentClass: ComponentClass<HoistProps> | FunctionComponent<HoistProps>;
50
56
 
51
57
  /**
52
58
  * Container component to be used to host this application.
53
- * This class determines the platform used by Hoist. The value should be imported from
59
+ *
60
+ * This class determines the platform used by Hoist - almost all apps will import and specify
54
61
  * either `@xh/hoist/desktop/AppContainer` or `@xh/hoist/mobile/AppContainer`.
55
62
  */
56
63
  containerClass: ComponentClass<HoistProps> | FunctionComponent<HoistProps>;
57
64
 
58
- /** True if the app should use the Hoist mobile toolkit.*/
59
- isMobileApp: boolean;
60
-
61
- /** True to show a login form on initialization when not authenticated. (default false) */
62
- enableLoginForm?: boolean;
63
-
64
65
  /**
65
- * True to show logout options in the app, for apps with auth schemes that can support this
66
- * operation (e.g. OAuth). (default false)
66
+ * True to disable Field-level XSS protection by default across all Stores/Fields in the app.
67
+ * For use with secure, internal apps that do not display arbitrary/external user input and
68
+ * have tight performance tolerances and/or load very large record sets.
69
+ * @see FieldSpec.disableXssProtection
67
70
  */
68
- enableLogout?: boolean;
71
+ disableXssProtection?: boolean;
69
72
 
70
73
  /**
71
- * Method for determining if user may access the app.
72
- * If a string, will be interpreted as the role required for basic UI access.
74
+ * True to show a login form on initialization when not authenticated. Default is `false` as
75
+ * most Hoist applications are expected to use OAuth or SSO for authn.
73
76
  */
74
- checkAccess: string | ((user: object) => boolean | {hasAccess: boolean; message: string});
77
+ enableLoginForm?: boolean;
75
78
 
76
79
  /**
77
- * Write a track log statement after the app has loaded and fully initialized?
78
- * Message will include elapsed time of asset loading and init.
80
+ * True to show logout options in the app, for apps with auth schemes that can support this
81
+ * operation (e.g. OAuth). Default is `false` as most Hoist applications are expected to use
82
+ * SSO or to run in internal environments where logout is not required / typical.
79
83
  */
80
- trackAppLoad?: boolean;
81
-
82
- /** True to enable Hoist websocket connectivity. (default false) */
83
- webSocketsEnabled?: boolean;
84
+ enableLogout?: boolean;
84
85
 
85
86
  /**
86
- * Optional custom Component to display when App has been suspended. The component will
87
- * receive a single prop -- onReactivate -- a callback called when the user has acknowledged
87
+ * Optional custom Component to display when the app has been suspended. The component will
88
+ * receive a single `onReactivate` prop, a no-arg callback fired when the user has acknowledged
88
89
  * the suspension and wishes to reload the app.
89
90
  */
90
91
  idlePanel?: ElementFactory | FunctionComponent | Component;
91
92
 
93
+ /** True if the app should use the Hoist mobile toolkit.*/
94
+ isMobileApp: boolean;
95
+
92
96
  /**
93
97
  * Optional custom Component to display when the user is denied access to app. Intended for
94
98
  * apps that implement custom auth flows. See also `lockoutMessage` for a more lightweight
@@ -96,12 +100,18 @@ export class AppSpec<T extends HoistAppModel = HoistAppModel> {
96
100
  */
97
101
  lockoutPanel?: ElementFactory | FunctionComponent | Component;
98
102
 
99
- /** Optional message to show on login form (see showLoginForm). */
100
- loginMessage?: string;
101
-
102
103
  /** Optional message to show users when denied access to app. */
103
104
  lockoutMessage?: string;
104
105
 
106
+ /** Optional message to show on login form, if `showLoginForm: true`. */
107
+ loginMessage?: string;
108
+
109
+ /**
110
+ * Root Model class for the application. Note this is a reference to the class itself, not an
111
+ * instance, and must extend {@link HoistAppModel}.
112
+ */
113
+ modelClass: new () => T;
114
+
105
115
  /**
106
116
  * True to show the built-in browser context menu when no app-specific menu would be shown
107
117
  * (e.g. from a Grid). False (the default) prevents the browser menu from being shown anywhere
@@ -110,67 +120,67 @@ export class AppSpec<T extends HoistAppModel = HoistAppModel> {
110
120
  showBrowserContextMenu?: boolean;
111
121
 
112
122
  /**
113
- * True to disable Field-level XSS protection by default across all Stores/Fields in the app.
114
- * For use with secure, internal apps that do not display arbitrary/external user input and
115
- * have tight performance tolerances and/or load very large record sets.
116
- * @see FieldConfig.disableXssProtection
123
+ * True (default) to write a track log statement after the app has loaded and fully
124
+ * initialized, including a breakdown of elapsed time throughout the init process.
117
125
  */
118
- disableXssProtection?: boolean;
126
+ trackAppLoad?: boolean;
127
+
128
+ /** True to enable Hoist websocket connectivity. */
129
+ webSocketsEnabled?: boolean;
119
130
 
120
131
  constructor({
132
+ authModelClass = HoistAuthModel,
133
+ checkAccess,
121
134
  clientAppCode = XH.appCode,
122
135
  clientAppName = XH.appName,
123
- modelClass,
124
136
  componentClass,
125
137
  containerClass,
126
- authModelClass = HoistAuthModel,
127
- isMobileApp,
128
- checkAccess,
138
+ disableXssProtection = false,
129
139
  enableLoginForm = false,
130
140
  enableLogout = false,
131
- trackAppLoad = true,
132
- webSocketsEnabled = false,
133
141
  idlePanel = null,
142
+ isMobileApp,
143
+ lockoutMessage = null,
134
144
  lockoutPanel = null,
135
145
  loginMessage = null,
136
- lockoutMessage = null,
146
+ modelClass,
137
147
  showBrowserContextMenu = false,
138
- disableXssProtection = false
148
+ trackAppLoad = true,
149
+ webSocketsEnabled = false
139
150
  }) {
140
- throwIf(!modelClass, 'A Hoist App must define a modelClass.');
141
151
  throwIf(!componentClass, 'A Hoist App must define a componentClass');
142
- throwIf(isNil(isMobileApp), 'A Hoist App must define isMobileApp');
152
+
143
153
  throwIf(
144
154
  !containerClass,
145
- `Please import and provide containerClass from "@xh/hoist/${
146
- isMobileApp ? 'mobile' : 'desktop'
147
- }/AppContainer".`
155
+ `Please import and provide containerClass from "@xh/hoist/${isMobileApp ? 'mobile' : 'desktop'}/AppContainer".`
148
156
  );
149
157
 
158
+ throwIf(!modelClass, 'A Hoist App must define a modelClass.');
159
+
160
+ throwIf(isNil(isMobileApp), 'A Hoist App must define isMobileApp');
161
+
150
162
  throwIf(
151
163
  !isString(checkAccess) && !isFunction(checkAccess),
152
164
  'A Hoist App must specify a required role string or a function for checkAccess.'
153
165
  );
154
166
 
167
+ this.authModelClass = authModelClass;
168
+ this.checkAccess = checkAccess;
155
169
  this.clientAppCode = clientAppCode;
156
170
  this.clientAppName = clientAppName;
157
-
158
- this.modelClass = modelClass;
159
171
  this.componentClass = componentClass;
160
172
  this.containerClass = containerClass;
161
- this.authModelClass = authModelClass;
162
- this.isMobileApp = isMobileApp;
163
- this.checkAccess = checkAccess;
164
-
165
- this.trackAppLoad = trackAppLoad;
166
- this.webSocketsEnabled = webSocketsEnabled;
173
+ this.disableXssProtection = disableXssProtection;
174
+ this.enableLoginForm = enableLoginForm;
175
+ this.enableLogout = enableLogout;
167
176
  this.idlePanel = idlePanel;
177
+ this.isMobileApp = isMobileApp;
178
+ this.lockoutMessage = lockoutMessage;
168
179
  this.lockoutPanel = lockoutPanel;
169
- this.enableLogout = enableLogout;
170
- this.enableLoginForm = enableLoginForm;
171
180
  this.loginMessage = loginMessage;
172
- this.lockoutMessage = lockoutMessage;
181
+ this.modelClass = modelClass;
173
182
  this.showBrowserContextMenu = showBrowserContextMenu;
174
- this.disableXssProtection = disableXssProtection;
183
+ this.trackAppLoad = trackAppLoad;
184
+ this.webSocketsEnabled = webSocketsEnabled;
175
185
  }
176
186
  }
@@ -9,13 +9,13 @@ import {HoistModel, PlainObject, XH} from './';
9
9
  /**
10
10
  * Base class for managing authentication lifecycle.
11
11
  *
12
- * Hoist will consult this model early in the initialization sequence, prior to full Hoist
12
+ * Hoist will consult this model early in the initialization sequence, prior to full Hoist
13
13
  * initialization. This means that several core services (identity, configs, prefs) will *not* be
14
14
  * available, but it provides the app a hook to do early service initialization or other work to
15
15
  * support flows such as OAuth.
16
16
  *
17
17
  * The base implementation of this class is minimal and would be adequate only for fully
18
- * transparent SSO based solutions such as NTLM. Applications wishing to provide custom
18
+ * transparent SSO based solutions such as NTLM. Applications wishing to provide custom
19
19
  * authentication should spec an extended version of this class in the {@link AppSpec} passed to
20
20
  * {@link XHApi#renderApp}.
21
21
  */
@@ -10,12 +10,23 @@ import '@xh/hoist/desktop/register';
10
10
  import {Icon} from '@xh/hoist/icon';
11
11
  import {withDefault} from '@xh/hoist/utils/js';
12
12
  import copy from 'clipboard-copy';
13
+ import {isString} from 'lodash';
13
14
 
14
15
  export interface ClipboardButtonProps extends ButtonProps {
15
16
  /** Function returning the text to copy. May be async. */
16
17
  getCopyText: () => string | Promise<string>;
17
- /** Message to be displayed in a toast when copy is complete. */
18
- successMessage?: string;
18
+
19
+ /**
20
+ * Message to be displayed in a toast should the copy operation fail, or `true` (default) to
21
+ * show a toast-based alert from `XH.handleException`. Spec `false` to fail silently.
22
+ */
23
+ errorMessage?: string | boolean;
24
+
25
+ /**
26
+ * Message to be displayed in a toast when copy is complete, or `true` for a default success
27
+ * confirmation. Default `false`
28
+ */
29
+ successMessage?: string | boolean;
19
30
  }
20
31
 
21
32
  /**
@@ -24,24 +35,36 @@ export interface ClipboardButtonProps extends ButtonProps {
24
35
  export const [ClipboardButton, clipboardButton] = hoistCmp.withFactory<ClipboardButtonProps>({
25
36
  displayName: 'ClipboardButton',
26
37
  model: false,
38
+
27
39
  render(props) {
28
- let {icon, onClick, text, getCopyText, successMessage, ...rest} = props;
40
+ let {icon, onClick, text, getCopyText, errorMessage, successMessage, ...rest} = props;
41
+ let errMsg = withDefault(errorMessage, true),
42
+ successMsg = withDefault(successMessage, false);
29
43
 
30
44
  if (!onClick) {
31
45
  onClick = async () => {
32
- const {successMessage, getCopyText} = props;
33
-
34
46
  try {
35
- const text = await getCopyText();
36
- await copy(text);
37
- if (successMessage) {
47
+ const copyText = await getCopyText();
48
+ await copy(copyText);
49
+ if (successMsg) {
50
+ successMsg = isString(successMsg) ? successMsg : 'Copied to clipboard';
38
51
  XH.toast({
39
- message: successMessage,
40
- icon: Icon.clipboard()
52
+ icon: Icon.clipboard(),
53
+ message: successMsg
41
54
  });
42
55
  }
43
56
  } catch (e) {
44
- XH.handleException(e, {showAlert: false});
57
+ if (errMsg) {
58
+ errMsg = isString(errMsg) ? errMsg : 'Error copying to clipboard';
59
+ XH.dangerToast({
60
+ icon: Icon.clipboard(),
61
+ message: errMsg
62
+ });
63
+ }
64
+ XH.handleException(e, {
65
+ message: 'Error copying to clipboard',
66
+ showAlert: false
67
+ });
45
68
  }
46
69
  };
47
70
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "75.0.0-SNAPSHOT.1753490062755",
3
+ "version": "75.0.0-SNAPSHOT.1753722797428",
4
4
  "description": "Hoist add-on for building and deploying React Applications.",
5
5
  "repository": "github:xh/hoist-react",
6
6
  "homepage": "https://xh.io",