@gooddata/sdk-ui-dashboard 10.37.0 → 10.38.0-alpha.1

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.
Files changed (60) hide show
  1. package/NOTICE +19 -28
  2. package/esm/__version.d.ts +1 -1
  3. package/esm/__version.d.ts.map +1 -1
  4. package/esm/__version.js +1 -1
  5. package/esm/__version.js.map +1 -1
  6. package/esm/_staging/dateFilterConfig/defaultConfig.d.ts +0 -4
  7. package/esm/_staging/dateFilterConfig/defaultConfig.d.ts.map +1 -1
  8. package/esm/_staging/dateFilterConfig/defaultConfig.js +294 -15
  9. package/esm/_staging/dateFilterConfig/defaultConfig.js.map +1 -1
  10. package/esm/_staging/sharedHooks/useFiltersNamings.d.ts.map +1 -1
  11. package/esm/_staging/sharedHooks/useFiltersNamings.js +2 -1
  12. package/esm/_staging/sharedHooks/useFiltersNamings.js.map +1 -1
  13. package/esm/locales.d.ts +3 -0
  14. package/esm/locales.d.ts.map +1 -1
  15. package/esm/locales.js +1 -0
  16. package/esm/locales.js.map +1 -1
  17. package/esm/model/store/filterContext/filterContextSelectors.js +6 -6
  18. package/esm/model/store/filterContext/filterContextSelectors.js.map +1 -1
  19. package/esm/model/store/index.d.ts +1 -1
  20. package/esm/model/store/index.d.ts.map +1 -1
  21. package/esm/model/store/index.js +1 -1
  22. package/esm/model/store/index.js.map +1 -1
  23. package/esm/model/store/topBar/topBarSelectors.d.ts +5 -0
  24. package/esm/model/store/topBar/topBarSelectors.d.ts.map +1 -1
  25. package/esm/model/store/topBar/topBarSelectors.js +8 -3
  26. package/esm/model/store/topBar/topBarSelectors.js.map +1 -1
  27. package/esm/presentation/dashboard/DashboardHeader/ShareDialogDashboardHeader.d.ts.map +1 -1
  28. package/esm/presentation/dashboard/DashboardHeader/ShareDialogDashboardHeader.js +2 -2
  29. package/esm/presentation/dashboard/DashboardHeader/ShareDialogDashboardHeader.js.map +1 -1
  30. package/esm/presentation/filterBar/filterBar/ResetFiltersButton.d.ts.map +1 -1
  31. package/esm/presentation/filterBar/filterBar/ResetFiltersButton.js +3 -3
  32. package/esm/presentation/filterBar/filterBar/ResetFiltersButton.js.map +1 -1
  33. package/esm/presentation/filterBar/filterBar/filterViews/FilterViews.d.ts.map +1 -1
  34. package/esm/presentation/filterBar/filterBar/filterViews/FilterViews.js +10 -3
  35. package/esm/presentation/filterBar/filterBar/filterViews/FilterViews.js.map +1 -1
  36. package/esm/presentation/flexibleLayout/DashboardLayoutWidget.js +1 -1
  37. package/esm/presentation/flexibleLayout/DashboardLayoutWidget.js.map +1 -1
  38. package/esm/presentation/localization/bundles/en-US.json +4 -0
  39. package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts +1 -0
  40. package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts.map +1 -1
  41. package/esm/presentation/localization/bundles/en-US.localization-bundle.js +1 -0
  42. package/esm/presentation/localization/bundles/en-US.localization-bundle.js.map +1 -1
  43. package/esm/presentation/presentationComponents/DashboardItems/DashboardItem.d.ts.map +1 -1
  44. package/esm/presentation/presentationComponents/DashboardItems/DashboardItem.js +1 -1
  45. package/esm/presentation/presentationComponents/DashboardItems/DashboardItem.js.map +1 -1
  46. package/esm/presentation/saveAs/DefaultSaveAsDialog/SaveAsDialogRenderer.d.ts +2 -19
  47. package/esm/presentation/saveAs/DefaultSaveAsDialog/SaveAsDialogRenderer.d.ts.map +1 -1
  48. package/esm/presentation/saveAs/DefaultSaveAsDialog/SaveAsDialogRenderer.js +36 -51
  49. package/esm/presentation/saveAs/DefaultSaveAsDialog/SaveAsDialogRenderer.js.map +1 -1
  50. package/esm/presentation/scheduledEmail/DefaultScheduledEmailDialog/components/MessageForm/MessageForm.js.map +1 -1
  51. package/esm/presentation/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/RecipientsSelectRenderer.d.ts +1 -40
  52. package/esm/presentation/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/RecipientsSelectRenderer.d.ts.map +1 -1
  53. package/esm/presentation/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/RecipientsSelectRenderer.js +232 -225
  54. package/esm/presentation/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/RecipientsSelectRenderer.js.map +1 -1
  55. package/esm/presentation/scheduledEmail/DefaultScheduledEmailDialog/components/Textarea.d.ts +3 -16
  56. package/esm/presentation/scheduledEmail/DefaultScheduledEmailDialog/components/Textarea.d.ts.map +1 -1
  57. package/esm/presentation/scheduledEmail/DefaultScheduledEmailDialog/components/Textarea.js +12 -22
  58. package/esm/presentation/scheduledEmail/DefaultScheduledEmailDialog/components/Textarea.js.map +1 -1
  59. package/esm/sdk-ui-dashboard.d.ts +6 -0
  60. package/package.json +16 -16
@@ -1,7 +1,7 @@
1
1
  // (C) 2019-2025 GoodData Corporation
2
2
  /* eslint-disable import/named,import/namespace */
3
3
  import { isAutomationUserRecipient, } from "@gooddata/sdk-model";
4
- import React from "react";
4
+ import React, { memo, useRef, useState, useEffect, useCallback, useMemo } from "react";
5
5
  import { FormattedMessage, useIntl } from "react-intl";
6
6
  import ReactSelect, { components as ReactSelectComponents, } from "react-select";
7
7
  import debounce from "lodash/debounce.js";
@@ -31,39 +31,45 @@ const TOOLTIP_ALIGN_POINTS = [
31
31
  offset: { x: 0, y: -2 },
32
32
  },
33
33
  ];
34
- export class RecipientsSelectRenderer extends React.PureComponent {
35
- recipientRef = React.createRef();
36
- constructor(props) {
37
- super(props);
38
- this.state = {
39
- menuOpen: false,
40
- minRecipientsError: false,
41
- focusedRecipientIndex: -1,
42
- };
43
- }
44
- componentDidMount() {
45
- const { current } = this.recipientRef;
34
+ export const RecipientsSelectRenderer = memo(function RecipientsSelectRenderer(props) {
35
+ const { isMulti, options, value, maxRecipients, className, usersError, allowOnlyLoggedUserRecipients, id, externalRecipientOverride, allowExternalRecipients, loggedUser, notificationChannel, showLabel, onChange, onLoad, isLoading, canListUsersInProject, allowEmptySelection, onKeyDownSubmit, } = props;
36
+ const [state, setState] = useState({
37
+ menuOpen: false,
38
+ minRecipientsError: false,
39
+ focusedRecipientIndex: -1,
40
+ });
41
+ const recipientRef = useRef(null);
42
+ const intl = useIntl();
43
+ const keyboardRecipientNavigationHandler = makeKeyboardNavigation({
44
+ onFocusPrevious: [{ code: ["ArrowLeft"] }],
45
+ onFocusNext: [{ code: ["ArrowRight"] }],
46
+ onSubmit: [{ code: ["Enter"] }],
47
+ onRecipientRemove: [{ code: ["Delete", "Backspace", "Enter"] }],
48
+ });
49
+ useEffect(() => {
50
+ const { current } = recipientRef;
46
51
  if (!current) {
47
52
  return;
48
53
  }
49
54
  // update owner component style after recipient rendered
50
55
  const ownerContainer = current.querySelector(".gd-owner-user");
51
- const style = this.getStyle();
56
+ const style = getStyle();
52
57
  if (ownerContainer && style) {
53
58
  ownerContainer.setAttribute("style", `max-width: ${style.maxWidth}px`);
54
59
  }
55
- }
56
- keyboardRecipientNavigationHandler = makeKeyboardNavigation({
57
- onFocusPrevious: [{ code: ["ArrowLeft"] }],
58
- onFocusNext: [{ code: ["ArrowRight"] }],
59
- onSubmit: [{ code: ["Enter"] }],
60
- onRecipientRemove: [{ code: ["Delete", "Backspace", "Enter"] }],
61
60
  });
62
- evaluateErrors() {
63
- const { value, maxRecipients, allowExternalRecipients, allowOnlyLoggedUserRecipients, loggedUser } = this.props;
61
+ const isEmailChannel = useCallback(() => {
62
+ return notificationChannel?.destinationType === "smtp";
63
+ }, [notificationChannel]);
64
+ const getHasEmail = useCallback((recipient) => {
65
+ return isEmailChannel() && isAutomationUserRecipient(recipient)
66
+ ? isEmail(recipient.email ?? "")
67
+ : true;
68
+ }, [isEmailChannel]);
69
+ const evaluateErrors = useCallback(() => {
64
70
  const maxRecipientsError = maxRecipients !== undefined && value.length > maxRecipients;
65
- const minRecipientsError = this.state.minRecipientsError;
66
- const someRecipientsMissingEmail = this.isEmailChannel()
71
+ const minRecipientsError = state.minRecipientsError;
72
+ const someRecipientsMissingEmail = isEmailChannel()
67
73
  ? value.some((v) => (isAutomationUserRecipient(v) ? !isEmail(v.email ?? "") : false))
68
74
  : false;
69
75
  const invalidExternalRecipients = value.filter((v) => v.type === "externalUser");
@@ -75,7 +81,7 @@ export class RecipientsSelectRenderer extends React.PureComponent {
75
81
  .join(", ");
76
82
  const authorOnlyError = allowOnlyLoggedUserRecipients &&
77
83
  ((value.length === 1 && value[0].id !== loggedUser?.id) || value.length > 1);
78
- const missingEmailRecipients = value.filter((v) => this.getHasEmail(v) === false);
84
+ const missingEmailRecipients = value.filter((v) => !getHasEmail(v));
79
85
  const missingEmailRecipientsValues = missingEmailRecipients.map((v) => v.name ?? v.id).join(", ");
80
86
  const missingEmailError = !!missingEmailRecipients.length;
81
87
  return {
@@ -89,58 +95,17 @@ export class RecipientsSelectRenderer extends React.PureComponent {
89
95
  missingEmailRecipientsValues,
90
96
  someRecipientsMissingEmail,
91
97
  };
92
- }
93
- render() {
94
- const { isMulti, options, value, maxRecipients, className, usersError, allowOnlyLoggedUserRecipients, id, externalRecipientOverride, } = this.props;
95
- const creatableSelectComponent = {
96
- ...ReactSelectComponents,
97
- IndicatorsContainer: this.renderEmptyContainer,
98
- Input: this.renderInputContainer,
99
- MultiValueContainer: this.renderMultiValueContainer,
100
- Menu: this.renderMenuOptions,
101
- MenuList: this.renderMenuList,
102
- Placeholder: this.renderEmptyContainer,
103
- NoOptionsMessage: this.renderNoOptionsContainer,
104
- MultiValueRemove: this.renderMultiValueRemove,
105
- };
106
- const { maxRecipientsError, minRecipientsError, invalidExternalError, invalidUnknownError, someRecipientsMissingEmail, authorOnlyError, missingEmailError, invalidRecipientsValues, missingEmailRecipientsValues, } = this.evaluateErrors();
107
- const showInputError = maxRecipientsError ||
108
- minRecipientsError ||
109
- invalidExternalError ||
110
- invalidUnknownError ||
111
- someRecipientsMissingEmail ||
112
- !!usersError ||
113
- authorOnlyError ||
114
- missingEmailError;
115
- const someExternalRecipients = value.some((v) => v.type === "externalUser");
116
- const renderExternalRecipientsNote = someExternalRecipients && !externalRecipientOverride;
117
- return (React.createElement("div", { className: cx("gd-input-component gd-recipients-field s-gd-notifications-channels-dialog-recipients", className) },
118
- this.props.showLabel ? (React.createElement("label", { htmlFor: id, className: "gd-label" },
119
- React.createElement(FormattedMessage, { id: "dialogs.schedule.email.recipients.label" }))) : null,
120
- React.createElement("div", { ref: this.recipientRef, className: "gd-input s-gd-recipients-value" },
121
- React.createElement(ReactSelect, { tabSelectsValue: false, id: id, className: cx("gd-recipients-container", {
122
- "gd-input-component--invalid": showInputError,
123
- }), classNamePrefix: "gd-recipients", components: creatableSelectComponent, formatOptionLabel: this.renderOptionLabel, filterOption: (opt, value) => {
124
- return matchRecipient(opt.data, value);
125
- }, isClearable: false, isDisabled: !isMulti, isMulti: isMulti, onChange:
126
- // using as any as it would be too tricky to type properly
127
- this.handleOnChange, onInputChange: this.onSearch, onMenuOpen: this.onMenuOpen, onMenuClose: () => this.setState({ menuOpen: false }), onKeyDown: this.handleKeyDown, options: options, onBlur: this.onBlur, value: value, getOptionValue: (o) => o.id, getOptionLabel: (o) => o.name ?? o.id, "aria-describedby": showInputError ? "gd-recipients-field-error" : undefined, "aria-invalid": showInputError }),
128
- showInputError ? (this.renderInputError({
129
- authorOnlyError,
130
- invalidExternalError,
131
- invalidUnknownError,
132
- maxRecipientsError,
133
- minRecipientsError,
134
- missingEmailError,
135
- invalidRecipientsValues,
136
- missingEmailRecipientsValues,
137
- maxRecipients,
138
- usersError,
139
- })) : renderExternalRecipientsNote ? (React.createElement("div", { className: "gd-recipients-field-note" },
140
- React.createElement(FormattedMessage, { id: "dialogs.schedule.email.recipients.note" }))) : allowOnlyLoggedUserRecipients ? (React.createElement("div", { className: "gd-recipients-field-note" },
141
- React.createElement(FormattedMessage, { id: "dialogs.schedule.email.destinationWarning" }))) : null)));
142
- }
143
- renderInputError = ({ authorOnlyError, invalidExternalError, invalidUnknownError, maxRecipientsError, minRecipientsError, missingEmailError, invalidRecipientsValues, missingEmailRecipientsValues, maxRecipients, usersError, }) => {
98
+ }, [
99
+ maxRecipients,
100
+ value,
101
+ state.minRecipientsError,
102
+ isEmailChannel,
103
+ allowExternalRecipients,
104
+ allowOnlyLoggedUserRecipients,
105
+ loggedUser?.id,
106
+ getHasEmail,
107
+ ]);
108
+ const renderInputError = useCallback(({ authorOnlyError, invalidExternalError, invalidUnknownError, maxRecipientsError, minRecipientsError, missingEmailError, invalidRecipientsValues, missingEmailRecipientsValues, maxRecipients, usersError, }) => {
144
109
  return (React.createElement("div", { id: "gd-recipients-field-error", className: "gd-recipients-field-error" },
145
110
  authorOnlyError ? (React.createElement("div", { className: "gd-recipients-field-error-item" },
146
111
  React.createElement(FormattedMessage, { id: "dialogs.schedule.email.user.invalid.onlyYou" }))) : null,
@@ -154,31 +119,26 @@ export class RecipientsSelectRenderer extends React.PureComponent {
154
119
  React.createElement(FormattedMessage, { id: "dialogs.schedule.email.user.missing.email", values: { recipients: missingEmailRecipientsValues } }))) : null,
155
120
  usersError ? (React.createElement("div", { className: "gd-recipients-field-error-item" },
156
121
  React.createElement(FormattedMessage, { id: "dialogs.schedule.email.usersLoad.error" }))) : null));
157
- };
158
- renderEmptyContainer = () => {
122
+ }, []);
123
+ const renderEmptyContainer = useCallback(() => {
159
124
  return null;
160
- };
161
- getStyle() {
162
- const { current } = this.recipientRef;
125
+ }, []);
126
+ const getStyle = useCallback(() => {
127
+ const { current } = recipientRef;
163
128
  const { width } = (!isEmpty(current) && current.getBoundingClientRect()) || { width: undefined };
164
129
  return {
165
130
  maxWidth: width ? width - PADDING : "100%",
166
131
  width,
167
132
  };
168
- }
169
- isEmailChannel() {
170
- const { notificationChannel } = this.props;
171
- return notificationChannel?.destinationType === "smtp";
172
- }
173
- renderNoOptionsContainer = () => {
174
- if (this.props.externalRecipientOverride) {
133
+ }, []);
134
+ const renderNoOptionsContainer = useCallback(() => {
135
+ if (externalRecipientOverride) {
175
136
  return null;
176
137
  }
177
138
  return (React.createElement("div", { className: "gd-recipients-no-match" },
178
139
  React.createElement(FormattedMessage, { id: "dialogs.schedule.email.user.noMatch" })));
179
- };
180
- renderMultiValueRemove = (props) => {
181
- const intl = useIntl();
140
+ }, [externalRecipientOverride]);
141
+ const renderMultiValueRemove = useCallback((props) => {
182
142
  const modifiedProps = {
183
143
  ...props,
184
144
  innerProps: {
@@ -187,43 +147,42 @@ export class RecipientsSelectRenderer extends React.PureComponent {
187
147
  },
188
148
  };
189
149
  return React.createElement(MultiValueRemove, { ...modifiedProps });
190
- };
191
- renderMenuOptions = (menuProps) => {
192
- const { isLoading } = this.props;
150
+ }, [intl]);
151
+ const renderLoadingIcon = useCallback((menuProps) => {
152
+ return (React.createElement(OverlayControllerProvider, { overlayController: overlayController },
153
+ React.createElement(Menu, { className: "s-gd-recipients-menu-container", ...menuProps },
154
+ React.createElement(LoadingMask, { height: LOADING_MENU_HEIGHT }))));
155
+ }, []);
156
+ const renderMenuOptionsContainer = useCallback((menuProps) => {
157
+ const style = getStyle();
158
+ return (React.createElement(OverlayControllerProvider, { overlayController: overlayController },
159
+ React.createElement(Overlay, { alignTo: ".gd-recipients-container", alignPoints: [{ align: "bc tc" }] },
160
+ React.createElement("div", { className: "gd-recipients-overlay", style: { width: style.width } },
161
+ React.createElement(Menu, { className: "s-gd-recipients-menu-container", ...menuProps }, menuProps.children)))));
162
+ }, [getStyle]);
163
+ const renderMenuOptions = useCallback((menuProps) => {
193
164
  const { getValue, selectProps: { inputValue }, } = menuProps;
194
165
  const selectedValues = getValue() || [];
195
166
  const selectedItemsCount = selectedValues.length;
196
167
  const areAllValuesSelected = false;
197
168
  if (isLoading) {
198
- return this.renderLoadingIcon(menuProps);
169
+ return renderLoadingIcon(menuProps);
199
170
  }
200
171
  if (!inputValue && (selectedItemsCount >= MAXIMUM_RECIPIENTS_RECEIVE || areAllValuesSelected)) {
201
- return this.renderEmptyContainer();
172
+ return renderEmptyContainer();
202
173
  }
203
- return this.renderMenuOptionsContainer(menuProps);
204
- };
205
- renderMenuList = (menuListProps) => {
174
+ return renderMenuOptionsContainer(menuProps);
175
+ }, [isLoading, renderEmptyContainer, renderLoadingIcon, renderMenuOptionsContainer]);
176
+ const renderMenuList = useCallback((menuListProps) => {
206
177
  const modifiedInnerProps = {
207
178
  ...menuListProps.innerProps,
208
179
  id: MENU_LIST_ID,
209
180
  };
210
181
  return React.createElement(MenuList, { ...menuListProps, innerProps: modifiedInnerProps });
211
- };
212
- renderMenuOptionsContainer = (menuProps) => {
213
- const style = this.getStyle();
214
- return (React.createElement(OverlayControllerProvider, { overlayController: overlayController },
215
- React.createElement(Overlay, { alignTo: ".gd-recipients-container", alignPoints: [{ align: "bc tc" }] },
216
- React.createElement("div", { className: "gd-recipients-overlay", style: { width: style.width } },
217
- React.createElement(Menu, { className: "s-gd-recipients-menu-container", ...menuProps }, menuProps.children)))));
218
- };
219
- renderLoadingIcon = (menuProps) => {
220
- return (React.createElement(OverlayControllerProvider, { overlayController: overlayController },
221
- React.createElement(Menu, { className: "s-gd-recipients-menu-container", ...menuProps },
222
- React.createElement(LoadingMask, { height: LOADING_MENU_HEIGHT }))));
223
- };
224
- renderMultiValueItemContainer = (label, removeIcon, recipientIndex, options = {}) => {
225
- const style = this.getStyle();
226
- const { focusedRecipientIndex } = this.state;
182
+ }, []);
183
+ const renderMultiValueItemContainer = useCallback((label, removeIcon, recipientIndex, options = {}) => {
184
+ const style = getStyle();
185
+ const { focusedRecipientIndex } = state;
227
186
  const render = () => {
228
187
  const showErrorIcon = options.noExternal ||
229
188
  options.invalidExternal ||
@@ -279,25 +238,19 @@ export class RecipientsSelectRenderer extends React.PureComponent {
279
238
  React.createElement(Bubble, { className: "bubble-primary", alignPoints: TOOLTIP_ALIGN_POINTS }, options.email)));
280
239
  }
281
240
  return render();
282
- };
283
- getHasEmail = (recipient) => {
284
- return this.isEmailChannel() && isAutomationUserRecipient(recipient)
285
- ? isEmail(recipient.email ?? "")
286
- : true;
287
- };
288
- renderMultiValueContainer = (multiValueProps) => {
289
- const { allowExternalRecipients, allowOnlyLoggedUserRecipients, loggedUser, value } = this.props;
241
+ }, [getStyle, state]);
242
+ const renderMultiValueContainer = useCallback((multiValueProps) => {
290
243
  const { data, children } = multiValueProps;
291
244
  // MultiValueRemove component from react-select
292
245
  const removeIcon = children[1];
293
246
  const name = data.name ?? data.id;
294
- const hasEmail = this.getHasEmail(data);
247
+ const hasEmail = getHasEmail(data);
295
248
  const noExternal = data.type === "externalUser" && !allowExternalRecipients;
296
249
  const invalidExternal = data.type === "unknownUser";
297
250
  const invalidLoggedUser = allowOnlyLoggedUserRecipients ? data.id !== loggedUser?.id : false;
298
251
  // Find the index of this recipient in the value array
299
252
  const recipientIndex = value.findIndex((recipient) => recipient.id === data.id);
300
- return this.renderMultiValueItemContainer(name, removeIcon, recipientIndex, {
253
+ return renderMultiValueItemContainer(name, removeIcon, recipientIndex, {
301
254
  hasEmail,
302
255
  noExternal,
303
256
  invalidLoggedUser,
@@ -305,18 +258,31 @@ export class RecipientsSelectRenderer extends React.PureComponent {
305
258
  type: data.type,
306
259
  email: data.email,
307
260
  });
308
- };
309
- renderOptionLabel = (recipient) => {
310
- const { allowExternalRecipients, externalRecipientOverride } = this.props;
261
+ }, [
262
+ allowExternalRecipients,
263
+ allowOnlyLoggedUserRecipients,
264
+ loggedUser,
265
+ value,
266
+ getHasEmail,
267
+ renderMultiValueItemContainer,
268
+ ]);
269
+ const renderRecipientValue = useCallback((recipient) => {
270
+ const email = isAutomationUserRecipient(recipient) ? (recipient.email ?? "") : "";
271
+ if (isEmail(email)) {
272
+ return (React.createElement("span", { className: "gd-recipient-option-value-item s-gd-recipient-option-value-item" }, email));
273
+ }
274
+ return renderEmptyContainer();
275
+ }, [renderEmptyContainer]);
276
+ const renderOptionLabel = useCallback((recipient) => {
311
277
  const displayName = recipient.name ?? recipient.id;
312
278
  const email = isAutomationUserRecipient(recipient) ? (recipient.email ?? "") : "";
313
- const value = this.renderRecipientValue(recipient);
279
+ const renderValue = renderRecipientValue(recipient);
314
280
  return (React.createElement(BubbleHoverTrigger, null,
315
281
  React.createElement("div", { className: "gd-recipient-option-item s-gd-recipient-option-item" },
316
282
  React.createElement("span", { className: "gd-recipient-option-label-item s-gd-recipient-option-label-item" }, displayName),
317
283
  allowExternalRecipients && recipient.type === "externalUser" ? (React.createElement("span", { className: "gd-recipient-quest" },
318
284
  "\u00A0",
319
- React.createElement(FormattedMessage, { id: "dialogs.schedule.email.user.guest" }))) : (value),
285
+ React.createElement(FormattedMessage, { id: "dialogs.schedule.email.user.guest" }))) : (renderValue),
320
286
  !externalRecipientOverride &&
321
287
  allowExternalRecipients &&
322
288
  recipient.type === "externalUser" ? (React.createElement("div", { className: "gd-recipient-option-label-external-warning" },
@@ -326,114 +292,52 @@ export class RecipientsSelectRenderer extends React.PureComponent {
326
292
  displayName,
327
293
  " ",
328
294
  isEmail(email) ? `(${email})` : "")));
329
- };
330
- renderRecipientValue = (recipient) => {
331
- const email = isAutomationUserRecipient(recipient) ? (recipient.email ?? "") : "";
332
- if (isEmail(email)) {
333
- return (React.createElement("span", { className: "gd-recipient-option-value-item s-gd-recipient-option-value-item" }, email));
334
- }
335
- return this.renderEmptyContainer();
336
- };
337
- renderInputContainer = (inputProps) => {
338
- const { isMulti } = this.props;
295
+ }, [allowExternalRecipients, externalRecipientOverride, renderRecipientValue]);
296
+ const renderInputContainer = useCallback((inputProps) => {
339
297
  if (!isMulti) {
340
- return this.renderEmptyContainer();
298
+ return renderEmptyContainer();
341
299
  }
342
300
  const props = {
343
301
  ...inputProps,
344
- id: this.props.id,
302
+ id: id,
345
303
  "aria-controls": MENU_LIST_ID,
346
304
  };
347
305
  return (React.createElement("div", { className: "gd-recipient-input s-gd-recipient-input" },
348
306
  React.createElement(Input, { ...props })));
349
- };
350
- handleKeyDown = (e) => {
351
- const { menuOpen } = this.state;
352
- if (isEscapeKey(e)) {
353
- e.stopPropagation();
354
- }
355
- if (!menuOpen) {
356
- this.handleKeyboardNavigation(e);
357
- }
358
- };
359
- handleOnChange = (selectedValues, actionTypes) => {
360
- const newSelectedValues = selectedValues;
361
- const { value, allowEmptySelection } = this.props;
362
- const { action } = actionTypes;
363
- if (value.length >= MAXIMUM_RECIPIENTS_RECEIVE &&
364
- (action === CREATE_OPTION || action === SELECT_OPTION)) {
365
- this.props.onChange?.(value);
366
- return;
367
- }
368
- if (newSelectedValues?.length === 0) {
369
- if (allowEmptySelection) {
370
- this.props.onChange?.([]);
371
- //this.setState({ minRecipientsError: true });
372
- }
373
- else {
374
- this.props.onChange?.([value[0]]);
375
- }
376
- return;
377
- }
378
- else {
379
- this.setState({ minRecipientsError: false });
380
- }
381
- this.props.onChange?.(newSelectedValues);
382
- };
383
- onBlur = () => {
384
- const { value, allowEmptySelection } = this.props;
385
- if (allowEmptySelection && value.length === 0) {
386
- this.setState({ minRecipientsError: true });
387
- }
388
- };
389
- loadUserListItems = (searchString) => {
390
- const { value, canListUsersInProject, onLoad } = this.props;
391
- const isRecipientAdded = this.isRecipientAdded(value, searchString);
392
- if (!canListUsersInProject || isRecipientAdded) {
307
+ }, [isMulti, id, renderEmptyContainer]);
308
+ const handleKeyboardRecipientRemove = useCallback((index) => {
309
+ if (index < 0 || index >= value.length) {
393
310
  return;
394
311
  }
395
- onLoad?.({ search: searchString });
396
- };
397
- onMenuOpen = () => {
398
- const { onLoad, canListUsersInProject, options } = this.props;
399
- const userListCount = options.length;
400
- this.setState({ menuOpen: true, focusedRecipientIndex: -1 });
401
- if (!userListCount && canListUsersInProject) {
402
- onLoad?.();
403
- }
404
- };
405
- onSearchCore = (searchString) => {
406
- this.loadUserListItems(searchString);
407
- };
408
- onSearch = debounce(this.onSearchCore, DELAY_TIME);
409
- isRecipientAdded = (value, searchKey) => {
410
- return value.some((recipient) => isEqual(recipient.id, searchKey));
411
- };
412
- handleKeyboardNavigation = (e) => {
413
- const { focusedRecipientIndex } = this.state;
414
- const { value, onKeyDownSubmit, onChange } = this.props;
312
+ const newValues = value.filter((_, i) => i !== index);
313
+ onChange?.(newValues);
314
+ const newFocusIndex = newValues.length === 0 ? -1 : Math.min(index, newValues.length - 1);
315
+ setState((prev) => ({ ...prev, focusedRecipientIndex: newFocusIndex }));
316
+ }, [value, onChange]);
317
+ const handleKeyboardNavigation = useCallback((e) => {
318
+ const { focusedRecipientIndex } = state;
415
319
  const totalRecipients = value.length;
416
- const keyboardHandler = this.keyboardRecipientNavigationHandler({
320
+ const keyboardHandler = keyboardRecipientNavigationHandler({
417
321
  onFocusPrevious: () => {
418
322
  if (focusedRecipientIndex === -1 && totalRecipients > 0) {
419
323
  const lastIndex = totalRecipients - 1;
420
- this.setState({ focusedRecipientIndex: lastIndex });
324
+ setState((prev) => ({ ...prev, focusedRecipientIndex: lastIndex }));
421
325
  }
422
326
  else if (focusedRecipientIndex > 0) {
423
327
  const prevIndex = focusedRecipientIndex - 1;
424
- this.setState({ focusedRecipientIndex: prevIndex });
328
+ setState((prev) => ({ ...prev, focusedRecipientIndex: prevIndex }));
425
329
  }
426
330
  else {
427
- this.setState({ focusedRecipientIndex: -1 });
331
+ setState((prev) => ({ ...prev, focusedRecipientIndex: -1 }));
428
332
  }
429
333
  },
430
334
  onFocusNext: () => {
431
335
  if (focusedRecipientIndex < totalRecipients - 1) {
432
336
  const nextIndex = focusedRecipientIndex + 1;
433
- this.setState({ focusedRecipientIndex: nextIndex });
337
+ setState((prev) => ({ ...prev, focusedRecipientIndex: nextIndex }));
434
338
  }
435
339
  else if (focusedRecipientIndex === totalRecipients - 1) {
436
- this.setState({ focusedRecipientIndex: -1 });
340
+ setState((prev) => ({ ...prev, focusedRecipientIndex: -1 }));
437
341
  }
438
342
  },
439
343
  onSubmit: () => {
@@ -441,7 +345,7 @@ export class RecipientsSelectRenderer extends React.PureComponent {
441
345
  onKeyDownSubmit?.(e);
442
346
  }
443
347
  else {
444
- this.handleKeyboardRecipientRemove(focusedRecipientIndex);
348
+ handleKeyboardRecipientRemove(focusedRecipientIndex);
445
349
  }
446
350
  },
447
351
  onRecipientRemove: () => {
@@ -450,24 +354,127 @@ export class RecipientsSelectRenderer extends React.PureComponent {
450
354
  onChange?.(newValues);
451
355
  }
452
356
  if (focusedRecipientIndex !== -1) {
453
- this.handleKeyboardRecipientRemove(focusedRecipientIndex);
357
+ handleKeyboardRecipientRemove(focusedRecipientIndex);
454
358
  }
455
359
  },
456
360
  onUnhandledKeyDown: () => {
457
- this.setState({ focusedRecipientIndex: -1 });
361
+ setState((prev) => ({ ...prev, focusedRecipientIndex: -1 }));
458
362
  },
459
363
  });
460
364
  keyboardHandler(e);
461
- };
462
- handleKeyboardRecipientRemove = (index) => {
463
- const { value, onChange } = this.props;
464
- if (index < 0 || index >= value.length) {
365
+ }, [
366
+ state,
367
+ value,
368
+ keyboardRecipientNavigationHandler,
369
+ onKeyDownSubmit,
370
+ handleKeyboardRecipientRemove,
371
+ onChange,
372
+ ]);
373
+ const handleKeyDown = useCallback((e) => {
374
+ const { menuOpen } = state;
375
+ if (isEscapeKey(e)) {
376
+ e.stopPropagation();
377
+ }
378
+ if (!menuOpen) {
379
+ handleKeyboardNavigation(e);
380
+ }
381
+ }, [handleKeyboardNavigation, state]);
382
+ const handleOnChange = useCallback((selectedValues, actionTypes) => {
383
+ const newSelectedValues = selectedValues;
384
+ const { action } = actionTypes;
385
+ if (value.length >= MAXIMUM_RECIPIENTS_RECEIVE &&
386
+ (action === CREATE_OPTION || action === SELECT_OPTION)) {
387
+ onChange?.(value);
465
388
  return;
466
389
  }
467
- const newValues = value.filter((_, i) => i !== index);
468
- onChange?.(newValues);
469
- const newFocusIndex = newValues.length === 0 ? -1 : Math.min(index, newValues.length - 1);
470
- this.setState({ focusedRecipientIndex: newFocusIndex });
390
+ if (newSelectedValues?.length === 0) {
391
+ if (allowEmptySelection) {
392
+ onChange?.([]);
393
+ }
394
+ else {
395
+ onChange?.([value[0]]);
396
+ }
397
+ return;
398
+ }
399
+ else {
400
+ setState((prev) => ({ ...prev, minRecipientsError: false }));
401
+ }
402
+ onChange?.(newSelectedValues);
403
+ }, [value, allowEmptySelection, onChange]);
404
+ const onBlur = useCallback(() => {
405
+ if (allowEmptySelection && value.length === 0) {
406
+ setState((prev) => ({ ...prev, minRecipientsError: true }));
407
+ }
408
+ }, [allowEmptySelection, value.length]);
409
+ const isRecipientAddedFn = useCallback((value, searchKey) => {
410
+ return value.some((recipient) => isEqual(recipient.id, searchKey));
411
+ }, []);
412
+ const loadUserListItems = useCallback((searchString) => {
413
+ const isRecipientAdded = isRecipientAddedFn(value, searchString);
414
+ if (!canListUsersInProject || isRecipientAdded) {
415
+ return;
416
+ }
417
+ onLoad?.({ search: searchString });
418
+ }, [isRecipientAddedFn, value, canListUsersInProject, onLoad]);
419
+ const onMenuOpen = useCallback(() => {
420
+ const userListCount = options.length;
421
+ setState((prev) => ({ ...prev, menuOpen: true, focusedRecipientIndex: -1 }));
422
+ if (!userListCount && canListUsersInProject) {
423
+ onLoad?.();
424
+ }
425
+ }, [options.length, canListUsersInProject, onLoad]);
426
+ const onSearchCore = useCallback((searchString) => {
427
+ loadUserListItems(searchString);
428
+ }, [loadUserListItems]);
429
+ const onSearch = useMemo(() => debounce((searchString) => {
430
+ onSearchCore(searchString);
431
+ }, DELAY_TIME), [onSearchCore]);
432
+ const creatableSelectComponent = {
433
+ ...ReactSelectComponents,
434
+ IndicatorsContainer: renderEmptyContainer,
435
+ Input: renderInputContainer,
436
+ MultiValueContainer: renderMultiValueContainer,
437
+ Menu: renderMenuOptions,
438
+ MenuList: renderMenuList,
439
+ Placeholder: renderEmptyContainer,
440
+ NoOptionsMessage: renderNoOptionsContainer,
441
+ MultiValueRemove: renderMultiValueRemove,
471
442
  };
472
- }
443
+ const { maxRecipientsError, minRecipientsError, invalidExternalError, invalidUnknownError, someRecipientsMissingEmail, authorOnlyError, missingEmailError, invalidRecipientsValues, missingEmailRecipientsValues, } = evaluateErrors();
444
+ const showInputError = maxRecipientsError ||
445
+ minRecipientsError ||
446
+ invalidExternalError ||
447
+ invalidUnknownError ||
448
+ someRecipientsMissingEmail ||
449
+ !!usersError ||
450
+ authorOnlyError ||
451
+ missingEmailError;
452
+ const someExternalRecipients = value.some((v) => v.type === "externalUser");
453
+ const renderExternalRecipientsNote = someExternalRecipients && !externalRecipientOverride;
454
+ return (React.createElement("div", { className: cx("gd-input-component gd-recipients-field s-gd-notifications-channels-dialog-recipients", className) },
455
+ showLabel ? (React.createElement("label", { htmlFor: id, className: "gd-label" },
456
+ React.createElement(FormattedMessage, { id: "dialogs.schedule.email.recipients.label" }))) : null,
457
+ React.createElement("div", { ref: recipientRef, className: "gd-input s-gd-recipients-value" },
458
+ React.createElement(ReactSelect, { tabSelectsValue: false, id: id, className: cx("gd-recipients-container", {
459
+ "gd-input-component--invalid": showInputError,
460
+ }), classNamePrefix: "gd-recipients", components: creatableSelectComponent, formatOptionLabel: renderOptionLabel, filterOption: (opt, value) => {
461
+ return matchRecipient(opt.data, value);
462
+ }, isClearable: false, isDisabled: !isMulti, isMulti: isMulti, onChange:
463
+ // using as any as it would be too tricky to type properly
464
+ handleOnChange, onInputChange: onSearch, onMenuOpen: onMenuOpen, onMenuClose: () => setState((prev) => ({ ...prev, menuOpen: false })), onKeyDown: handleKeyDown, options: options, onBlur: onBlur, value: value, getOptionValue: (o) => o.id, getOptionLabel: (o) => o.name ?? o.id, "aria-describedby": showInputError ? "gd-recipients-field-error" : undefined, "aria-invalid": showInputError }),
465
+ showInputError ? (renderInputError({
466
+ authorOnlyError,
467
+ invalidExternalError,
468
+ invalidUnknownError,
469
+ maxRecipientsError,
470
+ minRecipientsError,
471
+ missingEmailError,
472
+ invalidRecipientsValues,
473
+ missingEmailRecipientsValues,
474
+ maxRecipients,
475
+ usersError,
476
+ })) : renderExternalRecipientsNote ? (React.createElement("div", { className: "gd-recipients-field-note" },
477
+ React.createElement(FormattedMessage, { id: "dialogs.schedule.email.recipients.note" }))) : allowOnlyLoggedUserRecipients ? (React.createElement("div", { className: "gd-recipients-field-note" },
478
+ React.createElement(FormattedMessage, { id: "dialogs.schedule.email.destinationWarning" }))) : null)));
479
+ });
473
480
  //# sourceMappingURL=RecipientsSelectRenderer.js.map