@xh/hoist 75.0.0-SNAPSHOT.1753487024547 → 75.0.0-SNAPSHOT.1753720955374

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
 
@@ -25,68 +25,81 @@ export interface ViewUserState {
25
25
  }
26
26
  export interface ViewManagerConfig {
27
27
  /**
28
- * True (default) to allow user to opt in to automatically saving changes to their current view.
28
+ * Required discriminator for the particular class of views to be loaded and managed by this
29
+ * model. Used to set the `type` property on all JSONBlobs persisted by this model.
30
+ *
31
+ * Choose something descriptive and specific enough to be identifiable and allow for different
32
+ * ViewManagers to be added to your app in the future - e.g. `portfolioGridView` or
33
+ * `tradeBlotterDashboard`.
34
+ */
35
+ type: string;
36
+ /**
37
+ * Optional user-facing qualifier (default "default") for the special in-code default view
38
+ * option, if enabled. Will be prepended to `typeDisplayName`.
39
+ *
40
+ * A use case is to support a ViewManager persisted Dashboard, where the in-code default is an
41
+ * empty layout, this config is set to "New", and the `typeDisplayName` is set to "Dashboard".
42
+ * This results in a "New Dashboard" option in the menu, allowing users to quickly access a
43
+ * blank dashboard to start building from scratch, while forcing a save-as to persist.
29
44
  */
45
+ defaultDisplayName?: string;
46
+ /** True (default) to allow users to opt-in to auto-saving changes to their current view. */
30
47
  enableAutoSave?: boolean;
31
48
  /**
32
- * True (default) to allow the user to select a "Default" option that restores all persisted
33
- * objects to their in-code defaults. If not enabled, at least one saved view should be created
34
- * in advance, so that there is a clear initial selection for users without any private views.
49
+ * True (default) to allow the user to select a special view from the menu that restores all
50
+ * persisted objects to their in-code defaults. If not enabled, at least one globally shared
51
+ * view should be added to provide an initial selection for users without any private views.
35
52
  */
36
53
  enableDefault?: boolean;
37
54
  /**
38
55
  * True (default) to enable "global" views - i.e. views that are not owned by a user and are
39
- * available to all.
56
+ * available to all. At least some users should have `manageGlobal` set to true to allow
57
+ * creation and management of these views.
40
58
  */
41
59
  enableGlobal?: boolean;
42
- /**
43
- * True (default) to allow users to share their views with other users.
44
- */
60
+ /** True (default) to allow users to share their views with other users. */
45
61
  enableSharing?: boolean;
46
62
  /**
47
- * True (default) to save pending state to SessionStorage so that it can be restored across
48
- * browser refreshes. Unlike auto-save, this does not write to the database.
63
+ * User-facing qualifier for labelling globally shared views - default "global". A use case
64
+ * would be to set to the name of the company/team that manages these canonical views, e.g.
65
+ * "Acme Corp".
49
66
  */
50
- preserveUnsavedChanges?: boolean;
67
+ globalDisplayName?: string;
51
68
  /**
52
- * Function to determine the initial view for a user, when no view has already been persisted.
53
- * Will be passed a list of views available to the current user. Implementations where
54
- * enableDefault is set false should typically return some view, if any views are
55
- * available. If no view is returned, the control will be forced to fall back on the default.
69
+ * Function to determine the initial view for a user, when they have no prior view already
70
+ * persisted. Called with a list of views available to the current user.
56
71
  *
57
- * Must be set when enableDefault is false.
72
+ * Must be set when `enableDefault: false`. Developers should take care to return *some* view
73
+ * in this case, if any are available. If no view is returned, the control will be forced to
74
+ * fall back to the in-code default.
58
75
  */
59
76
  initialViewSpec?: (views: ViewInfo[]) => ViewInfo;
60
77
  /**
61
- * True to allow the user to publish or edit the global views. Apps are expected to
62
- * commonly set this based on user roles - e.g. `XH.getUser().hasRole('MANAGE_GRID_VIEWS')`.
78
+ * Optional discriminator for the particular area of an app in which this instance of the
79
+ * ViewManager appears, for apps that have multiple manager instances that load the same `type`
80
+ * of views. A particular `currentView` and `pendingValue` will be maintained for each instance,
81
+ * but all other options and the available library of views will be shared across the `type`.
63
82
  */
64
- manageGlobal?: Thunkable<boolean>;
83
+ instance?: string;
65
84
  /**
66
- * Required discriminator for the particular class of views to be loaded and managed by this
67
- * model. Set to something descriptive and specific enough to be identifiable and allow for
68
- * different viewManagers to be added to your app in the future - e.g. `portfolioGridView` or
69
- * `tradeBlotterDashboard`.
85
+ * True to allow the user to creat and manage Global views. Apps are expected to commonly set
86
+ * this based on user roles - e.g. `XH.getUser().hasRole('MANAGE_GRID_VIEWS')`.
70
87
  */
71
- type: string;
88
+ manageGlobal?: Thunkable<boolean>;
72
89
  /**
73
- * Optional sub-discriminator for the particular location in your app this instance of the
74
- * view manager appears in. A particular currentView and pendingValue will be maintained by
75
- * instance, but all other options, and the available library of views will be shared by type.
90
+ * True (default) to save pending state to SessionStorage so that it can be restored across
91
+ * browser refreshes. Unlike auto-save, this does not write to the database.
76
92
  */
77
- instance?: string;
93
+ preserveUnsavedChanges?: boolean;
78
94
  /**
79
- * Optional user-facing display name for the view type, displayed in the ViewManager menu
80
- * and associated management dialogs and prompts. Defaulted from `type` if not provided.
95
+ * User-facing display name for the type of views being managed - e.g. "report" or "dashboard".
96
+ * Displayed in the `ViewManager` menu and associated management dialogs and prompts.
97
+ * Defaulted from `type` if not provided.
81
98
  */
82
99
  typeDisplayName?: string;
83
100
  /**
84
- * Optional user-facing display name for describing global views. Defaults to 'global'
85
- */
86
- globalDisplayName?: string;
87
- /**
88
- * Optional key to pass a method that returns a customized BlueprintJS `menuItem` for listing
89
- * views in the ViewManager menu.
101
+ * Optional render function to customize the BlueprintJS `menuItem` shown for each view in the
102
+ * ViewManager menu.
90
103
  */
91
104
  viewMenuItemFn?: (view: ViewInfo, model: ViewManagerModel) => ReactNode;
92
105
  }
@@ -124,6 +137,7 @@ export declare class ViewManagerModel<T = PlainObject> extends HoistModel {
124
137
  readonly type: string;
125
138
  readonly instance: string;
126
139
  readonly typeDisplayName: string;
140
+ readonly defaultDisplayName: string;
127
141
  readonly globalDisplayName: string;
128
142
  readonly viewMenuItemFn: (view: ViewInfo, model: ViewManagerModel) => ReactNode;
129
143
  readonly enableAutoSave: boolean;
@@ -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.
@@ -52,79 +52,93 @@ export interface ViewUserState {
52
52
 
53
53
  export interface ViewManagerConfig {
54
54
  /**
55
- * True (default) to allow user to opt in to automatically saving changes to their current view.
55
+ * Required discriminator for the particular class of views to be loaded and managed by this
56
+ * model. Used to set the `type` property on all JSONBlobs persisted by this model.
57
+ *
58
+ * Choose something descriptive and specific enough to be identifiable and allow for different
59
+ * ViewManagers to be added to your app in the future - e.g. `portfolioGridView` or
60
+ * `tradeBlotterDashboard`.
61
+ */
62
+ type: string;
63
+
64
+ /**
65
+ * Optional user-facing qualifier (default "default") for the special in-code default view
66
+ * option, if enabled. Will be prepended to `typeDisplayName`.
67
+ *
68
+ * A use case is to support a ViewManager persisted Dashboard, where the in-code default is an
69
+ * empty layout, this config is set to "New", and the `typeDisplayName` is set to "Dashboard".
70
+ * This results in a "New Dashboard" option in the menu, allowing users to quickly access a
71
+ * blank dashboard to start building from scratch, while forcing a save-as to persist.
56
72
  */
73
+ defaultDisplayName?: string;
74
+
75
+ /** True (default) to allow users to opt-in to auto-saving changes to their current view. */
57
76
  enableAutoSave?: boolean;
58
77
 
59
78
  /**
60
- * True (default) to allow the user to select a "Default" option that restores all persisted
61
- * objects to their in-code defaults. If not enabled, at least one saved view should be created
62
- * in advance, so that there is a clear initial selection for users without any private views.
79
+ * True (default) to allow the user to select a special view from the menu that restores all
80
+ * persisted objects to their in-code defaults. If not enabled, at least one globally shared
81
+ * view should be added to provide an initial selection for users without any private views.
63
82
  */
64
83
  enableDefault?: boolean;
65
84
 
66
85
  /**
67
86
  * True (default) to enable "global" views - i.e. views that are not owned by a user and are
68
- * available to all.
87
+ * available to all. At least some users should have `manageGlobal` set to true to allow
88
+ * creation and management of these views.
69
89
  */
70
90
  enableGlobal?: boolean;
71
91
 
72
- /**
73
- * True (default) to allow users to share their views with other users.
74
- */
92
+ /** True (default) to allow users to share their views with other users. */
75
93
  enableSharing?: boolean;
76
94
 
77
95
  /**
78
- * True (default) to save pending state to SessionStorage so that it can be restored across
79
- * browser refreshes. Unlike auto-save, this does not write to the database.
96
+ * User-facing qualifier for labelling globally shared views - default "global". A use case
97
+ * would be to set to the name of the company/team that manages these canonical views, e.g.
98
+ * "Acme Corp".
80
99
  */
81
- preserveUnsavedChanges?: boolean;
100
+ globalDisplayName?: string;
82
101
 
83
102
  /**
84
- * Function to determine the initial view for a user, when no view has already been persisted.
85
- * Will be passed a list of views available to the current user. Implementations where
86
- * enableDefault is set false should typically return some view, if any views are
87
- * available. If no view is returned, the control will be forced to fall back on the default.
103
+ * Function to determine the initial view for a user, when they have no prior view already
104
+ * persisted. Called with a list of views available to the current user.
88
105
  *
89
- * Must be set when enableDefault is false.
106
+ * Must be set when `enableDefault: false`. Developers should take care to return *some* view
107
+ * in this case, if any are available. If no view is returned, the control will be forced to
108
+ * fall back to the in-code default.
90
109
  */
91
110
  initialViewSpec?: (views: ViewInfo[]) => ViewInfo;
92
111
 
93
112
  /**
94
- * True to allow the user to publish or edit the global views. Apps are expected to
95
- * commonly set this based on user roles - e.g. `XH.getUser().hasRole('MANAGE_GRID_VIEWS')`.
113
+ * Optional discriminator for the particular area of an app in which this instance of the
114
+ * ViewManager appears, for apps that have multiple manager instances that load the same `type`
115
+ * of views. A particular `currentView` and `pendingValue` will be maintained for each instance,
116
+ * but all other options and the available library of views will be shared across the `type`.
96
117
  */
97
- manageGlobal?: Thunkable<boolean>;
118
+ instance?: string;
98
119
 
99
120
  /**
100
- * Required discriminator for the particular class of views to be loaded and managed by this
101
- * model. Set to something descriptive and specific enough to be identifiable and allow for
102
- * different viewManagers to be added to your app in the future - e.g. `portfolioGridView` or
103
- * `tradeBlotterDashboard`.
121
+ * True to allow the user to creat and manage Global views. Apps are expected to commonly set
122
+ * this based on user roles - e.g. `XH.getUser().hasRole('MANAGE_GRID_VIEWS')`.
104
123
  */
105
- type: string;
124
+ manageGlobal?: Thunkable<boolean>;
106
125
 
107
126
  /**
108
- * Optional sub-discriminator for the particular location in your app this instance of the
109
- * view manager appears in. A particular currentView and pendingValue will be maintained by
110
- * instance, but all other options, and the available library of views will be shared by type.
127
+ * True (default) to save pending state to SessionStorage so that it can be restored across
128
+ * browser refreshes. Unlike auto-save, this does not write to the database.
111
129
  */
112
- instance?: string;
130
+ preserveUnsavedChanges?: boolean;
113
131
 
114
132
  /**
115
- * Optional user-facing display name for the view type, displayed in the ViewManager menu
116
- * and associated management dialogs and prompts. Defaulted from `type` if not provided.
133
+ * User-facing display name for the type of views being managed - e.g. "report" or "dashboard".
134
+ * Displayed in the `ViewManager` menu and associated management dialogs and prompts.
135
+ * Defaulted from `type` if not provided.
117
136
  */
118
137
  typeDisplayName?: string;
119
138
 
120
139
  /**
121
- * Optional user-facing display name for describing global views. Defaults to 'global'
122
- */
123
- globalDisplayName?: string;
124
-
125
- /**
126
- * Optional key to pass a method that returns a customized BlueprintJS `menuItem` for listing
127
- * views in the ViewManager menu.
140
+ * Optional render function to customize the BlueprintJS `menuItem` shown for each view in the
141
+ * ViewManager menu.
128
142
  */
129
143
  viewMenuItemFn?: (view: ViewInfo, model: ViewManagerModel) => ReactNode;
130
144
  }
@@ -168,6 +182,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
168
182
  readonly type: string;
169
183
  readonly instance: string;
170
184
  readonly typeDisplayName: string;
185
+ readonly defaultDisplayName: string;
171
186
  readonly globalDisplayName: string;
172
187
  readonly viewMenuItemFn: (view: ViewInfo, model: ViewManagerModel) => ReactNode;
173
188
  readonly enableAutoSave: boolean;
@@ -240,11 +255,12 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
240
255
  }
241
256
 
242
257
  get autoSaveUnavailableReason(): string {
243
- const {view, isViewAutoSavable, typeDisplayName, globalDisplayName} = this;
258
+ const {view, isViewAutoSavable, typeDisplayName, globalDisplayName, defaultDisplayName} =
259
+ this;
244
260
  if (isViewAutoSavable) return null;
245
261
  if (view.isGlobal) return `Cannot auto-save ${globalDisplayName} ${typeDisplayName}.`;
246
262
  if (view.isShared) return `Cannot auto-save shared ${typeDisplayName}.`;
247
- if (view.isDefault) return `Cannot auto-save default ${typeDisplayName}.`;
263
+ if (view.isDefault) return `Cannot auto-save ${defaultDisplayName} ${typeDisplayName}.`;
248
264
  if (XH.identityService.isImpersonating) return `Auto-save disabled during impersonation.`;
249
265
  return null;
250
266
  }
@@ -282,6 +298,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
282
298
  type,
283
299
  instance = 'default',
284
300
  typeDisplayName,
301
+ defaultDisplayName = 'default',
285
302
  globalDisplayName = 'global',
286
303
  viewMenuItemFn,
287
304
  manageGlobal = false,
@@ -303,6 +320,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
303
320
  this.type = type;
304
321
  this.instance = instance;
305
322
  this.typeDisplayName = lowerCase(typeDisplayName ?? genDisplayName(type));
323
+ this.defaultDisplayName = defaultDisplayName;
306
324
  this.globalDisplayName = globalDisplayName;
307
325
  this.viewMenuItemFn = viewMenuItemFn;
308
326
  this.manageGlobal = executeIfFunction(manageGlobal) ?? false;
@@ -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
  }
@@ -130,10 +130,12 @@ export const [ViewManager, viewManager] = hoistCmp.withFactory<ViewManagerProps>
130
130
 
131
131
  const menuButton = hoistCmp.factory<ViewManagerLocalModel>({
132
132
  render({model, icon, ...rest}) {
133
- const {view, typeDisplayName, isLoading} = model.parent;
133
+ const {view, defaultDisplayName, typeDisplayName, isLoading} = model.parent;
134
134
  return button({
135
135
  className: 'xh-view-manager__menu-button',
136
- text: view.isDefault ? `Default ${startCase(typeDisplayName)}` : view.name,
136
+ text: view.isDefault
137
+ ? `${startCase(defaultDisplayName)} ${startCase(typeDisplayName)}`
138
+ : view.name,
137
139
  icon: !isLoading
138
140
  ? icon
139
141
  : box({
@@ -36,7 +36,7 @@ export const viewMenu = hoistCmp.factory<ViewManagerLocalModel>({
36
36
  });
37
37
 
38
38
  function getNavMenuItems(model: ViewManagerModel): ReactNode[] {
39
- const {enableDefault, view, typeDisplayName, globalDisplayName} = model,
39
+ const {enableDefault, view, defaultDisplayName, typeDisplayName, globalDisplayName} = model,
40
40
  ownedViews = groupBy(filter(model.ownedViews, 'isPinned'), 'group'),
41
41
  globalViews = groupBy(filter(model.globalViews, 'isPinned'), 'group'),
42
42
  sharedViews = groupBy(filter(model.sharedViews, 'isPinned'), 'owner'),
@@ -69,7 +69,7 @@ function getNavMenuItems(model: ViewManagerModel): ReactNode[] {
69
69
  menuItem({
70
70
  className: 'xh-view-manager__menu-item',
71
71
  icon: view.isDefault ? Icon.check() : Icon.placeholder(),
72
- text: `Default ${startCase(typeDisplayName)}`,
72
+ text: `${startCase(defaultDisplayName)} ${startCase(typeDisplayName)}`,
73
73
  onClick: () => model.selectViewAsync(null).catchDefault()
74
74
  })
75
75
  );
@@ -7,7 +7,7 @@
7
7
 
8
8
  import {badge} from '@xh/hoist/cmp/badge';
9
9
  import {dateTimeCol, GridAutosizeMode, GridModel} from '@xh/hoist/cmp/grid';
10
- import {fragment, hbox, p, strong} from '@xh/hoist/cmp/layout';
10
+ import {br, fragment, hbox, p, strong} from '@xh/hoist/cmp/layout';
11
11
  import {TabContainerModel} from '@xh/hoist/cmp/tab';
12
12
  import {ViewInfo, ViewManagerModel, ViewUpdateSpec} from '@xh/hoist/cmp/viewmanager';
13
13
  import {HoistModel, LoadSpec, managed, TaskObserver, XH} from '@xh/hoist/core';
@@ -338,10 +338,15 @@ export class ManageDialogModel extends HoistModel {
338
338
  model: this.ownedGridModel,
339
339
  helpText: fragment(
340
340
  Icon.user(),
341
- `This tab shows ${views} you have created. Pinned ${views} are shown in your menu for quick access. Set a group on ${views} to show them together in a sub-menu. `,
342
- enableSharing
343
- ? `Opt-in to sharing any of your ${views} to make them discoverable by other users.`
344
- : ''
341
+ `This tab shows ${views} you have created.`,
342
+ br(),
343
+ `Pin ${views} to your menu for quick access. Use groups to nest them under sub-menus.`,
344
+ ...(enableSharing
345
+ ? [
346
+ br(),
347
+ `Opt-in to sharing any of your ${views} to make them discoverable by other users.`
348
+ ]
349
+ : [''])
345
350
  )
346
351
  })
347
352
  }
@@ -355,7 +360,9 @@ export class ManageDialogModel extends HoistModel {
355
360
  model: this.globalGridModel,
356
361
  helpText: fragment(
357
362
  Icon.globe(),
358
- `This tab shows ${globalViews} available to everyone. ${capitalize(globalViews)} can be pinned by default so they appear automatically in everyone's menu, but you can choose which ${views} you would like to see by pinning/unpinning them at any time.`
363
+ `This tab shows ${globalViews} available to everyone.`,
364
+ br(),
365
+ `${capitalize(globalViews)} can be set to appear automatically in everyone's menu, but you can choose which ${views} you would like to see by pinning/unpinning them at any time.`
359
366
  )
360
367
  })
361
368
  });
@@ -369,7 +376,9 @@ export class ManageDialogModel extends HoistModel {
369
376
  model: this.sharedGridModel,
370
377
  helpText: fragment(
371
378
  Icon.users(),
372
- `This tab shows ${views} shared by other ${XH.appName} users. You can pin these ${views} to add them to your menu and access them directly. Only the owner will be able to save changes to a shared ${view}, but you can save as a copy to make it your own.`
379
+ `This tab shows ${views} shared by other ${XH.appName} users.`,
380
+ br(),
381
+ `You can pin these ${views} to your menu for quick access. Only the owner will be able to save changes to a shared ${view}, but you can save a copy to make it your own.`
373
382
  )
374
383
  })
375
384
  });
@@ -79,10 +79,12 @@ export const viewPanel = hoistCmp.factory({
79
79
  }),
80
80
  formField({
81
81
  field: 'isDefaultPinned',
82
- label: 'Pin by default?',
83
- labelWidth: 110,
82
+ label: null,
84
83
  inline: true,
85
- item: switchInput(),
84
+ item: switchInput({
85
+ label: `Pin to everyone's menu by default`,
86
+ labelSide: 'left'
87
+ }),
86
88
  omit: !isGlobal || !isEditable
87
89
  }),
88
90
  vspacer(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "75.0.0-SNAPSHOT.1753487024547",
3
+ "version": "75.0.0-SNAPSHOT.1753720955374",
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",