@box/user-selector 1.68.2 → 1.70.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.
@@ -0,0 +1,23 @@
1
+ const c = "__combobox_overlay_header__", l = "__combobox_overlay_separator__";
2
+ function o(a, e, t) {
3
+ if (!a || a.length === 0)
4
+ return a ?? void 0;
5
+ let _ = [...a];
6
+ if (t >= 0 && t < _.length - 1) {
7
+ const n = {
8
+ name: "",
9
+ value: l
10
+ };
11
+ _ = [..._.slice(0, t + 1), n, ..._.slice(t + 1)];
12
+ }
13
+ return e && _.length > 0 && t >= 0 && (_ = [{
14
+ name: "",
15
+ value: c,
16
+ label: e
17
+ }, ..._]), _;
18
+ }
19
+ export {
20
+ c as O,
21
+ l as a,
22
+ o as b
23
+ };
package/dist/esm/index.js CHANGED
@@ -1,6 +1,9 @@
1
- import { UserSelectorContainer as o } from "./lib/user-selector-container.js";
2
- import { UserSelectorHeadless as s } from "./lib/user-selector-headless/user-selector-headless.js";
1
+ import { O as o, a as A } from "../chunks/buildOptionsWithOverlay.js";
2
+ import { UserSelectorContainer as s } from "./lib/user-selector-container.js";
3
+ import { UserSelectorHeadless as a } from "./lib/user-selector-headless/user-selector-headless.js";
3
4
  export {
4
- o as UserSelectorContainer,
5
- s as UserSelectorHeadless
5
+ o as OVERLAY_HEADER_VALUE,
6
+ A as OVERLAY_SEPARATOR_VALUE,
7
+ s as UserSelectorContainer,
8
+ a as UserSelectorHeadless
6
9
  };
@@ -1,77 +1,108 @@
1
- import { Avatar as i, IconBadge as y } from "@box/blueprint-web";
2
- import { ComboboxWithApi as I } from "@box/combobox-with-api";
3
- import O from "clsx";
4
- import { useIntl as M } from "react-intl";
5
- import a from "./messages.js";
6
- import { UserSelectorComboboxOption as V } from "./user-selector-list-item.js";
7
- import { defaultRenderCustomOption as R } from "./utils/defaultRenderCustomOption.js";
8
- import { getInitials as S } from "./utils/getInitials.js";
9
- import { isUserContactType as l } from "./utils/isUserContactType.js";
10
- import { jsx as t } from "react/jsx-runtime";
11
- import '../../styles/user-selector.css';const _ = "_textArea_shf88_1", N = {
12
- textArea: _
13
- }, D = (m) => {
1
+ import H from "clsx";
2
+ import { useCallback as z } from "react";
3
+ import { useIntl as D } from "react-intl";
4
+ import { Combobox as p, Text as M, Divider as N, Avatar as h, IconBadge as T } from "@box/blueprint-web";
5
+ import { ComboboxWithApi as W } from "@box/combobox-with-api";
6
+ import u from "./messages.js";
7
+ import { b as w, O as b, a as y } from "../../chunks/buildOptionsWithOverlay.js";
8
+ import { UserSelectorComboboxOption as Y } from "./user-selector-list-item.js";
9
+ import { defaultRenderCustomOption as j } from "./utils/defaultRenderCustomOption.js";
10
+ import { getInitials as k } from "./utils/getInitials.js";
11
+ import { isUserContactType as O } from "./utils/isUserContactType.js";
12
+ import { jsx as r } from "react/jsx-runtime";
13
+ import '../../styles/user-selector.css';const B = "_overlayHeader_zdn0a_1", P = "_overlaySeparatorOption_zdn0a_8", q = "_textArea_zdn0a_15", m = {
14
+ overlayHeader: B,
15
+ overlaySeparatorOption: P,
16
+ textArea: q
17
+ }, se = (x) => {
14
18
  const {
15
- fetchedAvatarUrls: c,
16
- fetchUsers: f,
17
- onSelectedUsersChange: u,
18
- selectedUsers: p,
19
- variant: d,
20
- className: x,
21
- defaultOptions: g,
22
- renderCustomOption: h = R,
23
- ...v
24
- } = m, r = M(), s = (e) => {
25
- if (l(e)) {
26
- const {
27
- name: U,
28
- isExternalUser: b,
29
- id: o
30
- } = e, n = /* @__PURE__ */ t(i, {
31
- alt: r.formatMessage(a.userAvatar),
32
- colorIndex: o,
33
- src: c[o],
34
- text: S(U)
19
+ fetchedAvatarUrls: A,
20
+ fetchUsers: v,
21
+ onSelectedUsersChange: g,
22
+ selectedUsers: i,
23
+ variant: _,
24
+ className: C,
25
+ defaultOptions: S,
26
+ overlayDividerIndex: a,
27
+ overlayTitle: c,
28
+ renderCustomOption: U = j,
29
+ ...V
30
+ } = x, d = D(), f = (e) => {
31
+ if (!(e.value === b || e.value === y)) {
32
+ if (O(e)) {
33
+ const {
34
+ name: t,
35
+ isExternalUser: l,
36
+ id: n
37
+ } = e, s = /* @__PURE__ */ r(h, {
38
+ alt: d.formatMessage(u.userAvatar),
39
+ colorIndex: n,
40
+ src: A[n],
41
+ text: k(t)
42
+ });
43
+ return l ? /* @__PURE__ */ r(T, {
44
+ offset: {
45
+ right: -4,
46
+ bottom: -2
47
+ },
48
+ size: "medium",
49
+ variant: "collaborator-external",
50
+ children: s
51
+ }) : s;
52
+ }
53
+ return /* @__PURE__ */ r(h, {
54
+ ...e.avatar
35
55
  });
36
- return b ? /* @__PURE__ */ t(y, {
37
- offset: {
38
- right: -4,
39
- bottom: -2
40
- },
41
- size: "medium",
42
- variant: "collaborator-external",
43
- children: n
44
- }) : n;
45
56
  }
46
- return /* @__PURE__ */ t(i, {
47
- ...e.avatar
48
- });
49
- }, A = (e) => {
50
- if (!l(e))
57
+ }, E = (e) => {
58
+ if (!O(e))
51
59
  return e.chipVariant;
52
- }, C = (e) => /* @__PURE__ */ t(V, {
53
- renderAvatar: s,
54
- renderCustomOption: h,
60
+ }, R = z(async (e) => {
61
+ const t = await v(e);
62
+ if (!c || a === void 0 || a === null || a < 0)
63
+ return t;
64
+ const l = new Set(i.map((o) => o.value)), n = t.filter((o) => !l.has(o.value)), s = t.slice(0, a + 1).filter((o) => !l.has(o.value)).length, L = s > 0 ? s - 1 : -1;
65
+ return w(n, c, L);
66
+ }, [v, c, a, i]), I = (e) => e.value === b && "label" in e ? /* @__PURE__ */ r(p.Option, {
67
+ className: m.overlayHeader,
68
+ disabled: !0,
69
+ value: e.value,
70
+ children: /* @__PURE__ */ r(M, {
71
+ as: "span",
72
+ variant: "bodySmallSemibold",
73
+ color: "textOnLightSecondary",
74
+ children: e.label
75
+ })
76
+ }) : e.value === y ? /* @__PURE__ */ r(p.Option, {
77
+ className: m.overlaySeparatorOption,
78
+ disabled: !0,
79
+ value: e.value,
80
+ children: /* @__PURE__ */ r(N, {
81
+ "aria-hidden": !0
82
+ })
83
+ }) : /* @__PURE__ */ r(Y, {
84
+ renderAvatar: f,
85
+ renderCustomOption: U,
55
86
  userContact: e
56
87
  });
57
- return /* @__PURE__ */ t(I, {
88
+ return /* @__PURE__ */ r(W, {
58
89
  as: "input",
59
- className: O(x, d === "textarea" && N.textArea),
60
- defaultOptions: g,
61
- displayAvatar: s,
62
- displayChipVariant: A,
90
+ className: H(C, _ === "textarea" && m.textArea),
91
+ defaultOptions: S,
92
+ displayAvatar: f,
93
+ displayChipVariant: E,
63
94
  displayValue: (e) => e.name,
64
- fetcher: f,
95
+ fetcher: R,
65
96
  freeInput: !1,
66
- loadingAriaLabel: r.formatMessage(a.loading),
97
+ loadingAriaLabel: d.formatMessage(u.loading),
67
98
  multiselect: !0,
68
- noResultMessage: r.formatMessage(a.noResults),
69
- onValueChange: u,
70
- renderOption: C,
71
- value: p,
72
- ...v
99
+ noResultMessage: d.formatMessage(u.noResults),
100
+ onValueChange: g,
101
+ renderOption: I,
102
+ value: i,
103
+ ...V
73
104
  });
74
105
  };
75
106
  export {
76
- D as default
107
+ se as default
77
108
  };
@@ -0,0 +1,4 @@
1
+ import { b as r } from "../../../chunks/buildOptionsWithOverlay.js";
2
+ export {
3
+ r as buildOptionsWithOverlay
4
+ };
@@ -1,12 +1,12 @@
1
1
  export default {
2
- "groupSharedFeatures.userSelector.emptyMessage": "⟦萬萬萬萬萬萬 Ñο čοĺĺãвόгåťōřś ƒöūηď 國國國國國國⟧",
3
- "groupSharedFeatures.userSelector.errorMessage": "⟦萬萬萬萬萬萬萬萬 Шë ŵέяė ūиάьľέ ţö ľøαď ųѕéřş 國國國國國國國國⟧",
4
- "groupSharedFeatures.userSelector.errorMessageDescription": "⟦萬萬萬萬萬萬 Pļéáśé τŗŷ āġąìň ľáťěґ 國國國國國國⟧",
5
- "groupSharedFeatures.userSelector.inviteCollaborators": "⟦萬萬萬萬萬 Іηνĭťε Çόļĺǻвőřαŧøяş 國國國國國⟧",
6
- "groupSharedFeatures.userSelector.inviteCollaboratorsTooltip": "⟦萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬 Ţнιş Ρеяѕой ďòĕѕň'ţ ħâνé ǻćčёśş áηď ẁøň'ť ьє лöťíƒįεď ûńļēŝŝ íńνĭτéď. 國國國國國國國國國國國國國國國國國國國國⟧",
7
- "groupSharedFeatures.userSelector.loading": "⟦萬萬 Ļōāďĭηĝ 國國⟧",
8
- "groupSharedFeatures.userSelector.noResults": "⟦萬萬 Ńõ яєѕűĺţś 國國⟧",
9
- "groupSharedFeatures.userSelector.tryAgain": "⟦萬萬 ŢґУ ăĝåίη 國國⟧",
10
- "groupSharedFeatures.userSelector.userAvatar": "⟦萬萬萬 Ųѕêя ανàŧäґ 國國國⟧",
2
+ "groupSharedFeatures.userSelector.emptyMessage": "⟦萬萬萬萬萬萬 Ŋö çöĺľáвøяāţόŕŝ ƒõμйď 國國國國國國⟧",
3
+ "groupSharedFeatures.userSelector.errorMessage": "⟦萬萬萬萬萬萬萬萬 Ŵè ŵéŕέ ŭʼnάьļè ťø ĺöǻď ŭѕěŕѕ 國國國國國國國國⟧",
4
+ "groupSharedFeatures.userSelector.errorMessageDescription": "⟦萬萬萬萬萬萬 Pļеâѕë ŧŕÿ άġäïπ ļáŧёř 國國國國國國⟧",
5
+ "groupSharedFeatures.userSelector.inviteCollaborators": "⟦萬萬萬萬萬 Įπνϊŧе Ċôĺĺâвόřäţòѓѕ 國國國國國⟧",
6
+ "groupSharedFeatures.userSelector.inviteCollaboratorsTooltip": "⟦萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬 Τħĩѕ Ρėяѕõń ďôέѕŋ'ŧ ħäνē ãćĉěşѕ άńď ẁοή'τ вě ńοŧīƒιёď ūηľėśŝ ιηνìŧеď. 國國國國國國國國國國國國國國國國國國國國⟧",
7
+ "groupSharedFeatures.userSelector.loading": "⟦萬萬 Ĺóǻďілĝ 國國⟧",
8
+ "groupSharedFeatures.userSelector.noResults": "⟦萬萬 Νõ ґέѕùļťś 國國⟧",
9
+ "groupSharedFeatures.userSelector.tryAgain": "⟦萬萬 Ŧяÿ ãĝāïη 國國⟧",
10
+ "groupSharedFeatures.userSelector.userAvatar": "⟦萬萬萬 Ŭѕέѓ ãνąťάг 國國國⟧",
11
11
  "groupSharedFeatures.userSelector.userName": "⟦萬萬萬萬萬萬萬 {name} {isCurrentUser, select, true { (mê)} other {}} 國國國國國國國⟧"
12
12
  }
@@ -1,20 +1,20 @@
1
1
  # Text displayed when no results are found
2
- groupSharedFeatures.userSelector.emptyMessage = ⟦萬萬萬萬萬萬 Ñο čοĺĺãвόгåťōřś ƒöūηď 國國國國國國⟧
2
+ groupSharedFeatures.userSelector.emptyMessage = ⟦萬萬萬萬萬萬 Ŋö çöĺľáвøяāţόŕŝ ƒõμйď 國國國國國國⟧
3
3
  # Text displayed when an error occurs
4
- groupSharedFeatures.userSelector.errorMessage = ⟦萬萬萬萬萬萬萬萬 Шë ŵέяė ūиάьľέ ţö ľøαď ųѕéřş 國國國國國國國國⟧
4
+ groupSharedFeatures.userSelector.errorMessage = ⟦萬萬萬萬萬萬萬萬 Ŵè ŵéŕέ ŭʼnάьļè ťø ĺöǻď ŭѕěŕѕ 國國國國國國國國⟧
5
5
  # Text displayed when an error occurs
6
- groupSharedFeatures.userSelector.errorMessageDescription = ⟦萬萬萬萬萬萬 Pļéáśé τŗŷ āġąìň ľáťěґ 國國國國國國⟧
6
+ groupSharedFeatures.userSelector.errorMessageDescription = ⟦萬萬萬萬萬萬 Pļеâѕë ŧŕÿ άġäïπ ļáŧёř 國國國國國國⟧
7
7
  # Text displayed when no results are found and the user can invite collaborators
8
- groupSharedFeatures.userSelector.inviteCollaborators = ⟦萬萬萬萬萬 Іηνĭťε Çόļĺǻвőřαŧøяş 國國國國國⟧
8
+ groupSharedFeatures.userSelector.inviteCollaborators = ⟦萬萬萬萬萬 Įπνϊŧе Ċôĺĺâвόřäţòѓѕ 國國國國國⟧
9
9
  # Tooltip text displayed when a user is not a collaborator
10
- groupSharedFeatures.userSelector.inviteCollaboratorsTooltip = ⟦萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬 Ţнιş Ρеяѕой ďòĕѕň'ţ ħâνé ǻćčёśş áηď ẁøň'ť ьє лöťíƒįεď ûńļēŝŝ íńνĭτéď. 國國國國國國國國國國國國國國國國國國國國⟧
10
+ groupSharedFeatures.userSelector.inviteCollaboratorsTooltip = ⟦萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬萬 Τħĩѕ Ρėяѕõń ďôέѕŋ'ŧ ħäνē ãćĉěşѕ άńď ẁοή'τ вě ńοŧīƒιёď ūηľėśŝ ιηνìŧеď. 國國國國國國國國國國國國國國國國國國國國⟧
11
11
  # Aria label for loading indicator
12
- groupSharedFeatures.userSelector.loading = ⟦萬萬 Ļōāďĭηĝ 國國⟧
12
+ groupSharedFeatures.userSelector.loading = ⟦萬萬 Ĺóǻďілĝ 國國⟧
13
13
  # Text displayed when no results are found
14
- groupSharedFeatures.userSelector.noResults = ⟦萬萬 Ńõ яєѕűĺţś 國國⟧
14
+ groupSharedFeatures.userSelector.noResults = ⟦萬萬 Νõ ґέѕùļťś 國國⟧
15
15
  # Text for the "Try again" button
16
- groupSharedFeatures.userSelector.tryAgain = ⟦萬萬 ŢґУ ăĝåίη 國國⟧
16
+ groupSharedFeatures.userSelector.tryAgain = ⟦萬萬 Ŧяÿ ãĝāïη 國國⟧
17
17
  # Alt text for user avatar image
18
- groupSharedFeatures.userSelector.userAvatar = ⟦萬萬萬 Ųѕêя ανàŧäґ 國國國⟧
18
+ groupSharedFeatures.userSelector.userAvatar = ⟦萬萬萬 Ŭѕέѓ ãνąťάг 國國國⟧
19
19
  # User name with optional "me" marker for current user
20
20
  groupSharedFeatures.userSelector.userName = ⟦萬萬萬萬萬萬萬 {name} {isCurrentUser, select, true { (mê)} other {}} 國國國國國國國⟧
@@ -1 +1 @@
1
- ._textArea_shf88_1._textArea_shf88_1._textArea_shf88_1 [data-testid=combobox-container]{align-items:flex-start;box-sizing:border-box;min-height:80px;max-height:none}._textArea_shf88_1._textArea_shf88_1._textArea_shf88_1 [data-testid=combobox-container] input{padding:var(--space-1) 0}
1
+ ._overlayHeader_zdn0a_1._overlayHeader_zdn0a_1{padding-block:var(--bp-space-010, var(--space-1))}._overlayHeader_zdn0a_1._overlayHeader_zdn0a_1[aria-disabled]{opacity:1}._overlaySeparatorOption_zdn0a_8._overlaySeparatorOption_zdn0a_8{padding-block:0}._overlaySeparatorOption_zdn0a_8._overlaySeparatorOption_zdn0a_8[aria-disabled]{opacity:1}._textArea_zdn0a_15._textArea_zdn0a_15._textArea_zdn0a_15 [data-testid=combobox-container]{align-items:flex-start;box-sizing:border-box;min-height:80px;max-height:none}._textArea_zdn0a_15._textArea_zdn0a_15._textArea_zdn0a_15 [data-testid=combobox-container] input{padding:var(--space-1) 0}
@@ -1,4 +1,7 @@
1
1
  import { ReactElement, SVGProps } from 'react';
2
+ /** Option value for overlay header/divider rows (non-selectable). */
3
+ export declare const OVERLAY_HEADER_VALUE = "__combobox_overlay_header__";
4
+ export declare const OVERLAY_SEPARATOR_VALUE = "__combobox_overlay_separator__";
2
5
  export type UserContactType = {
3
6
  chipVariant?: 'error' | 'default' | 'variable';
4
7
  email: string;
@@ -28,6 +31,15 @@ export type CustomUserContactType = {
28
31
  hideOnClick?: boolean;
29
32
  chipVariant?: 'default' | 'error' | 'variable';
30
33
  };
34
+ export type OverlayHeaderOption = {
35
+ name: string;
36
+ value: typeof OVERLAY_HEADER_VALUE;
37
+ label: string;
38
+ };
39
+ export type OverlaySeparatorOption = {
40
+ name: string;
41
+ value: typeof OVERLAY_SEPARATOR_VALUE;
42
+ };
31
43
  export type UserSelectorOption = UserContactType | CustomUserContactType;
32
44
  export type FetchedAvatarUrls = {
33
45
  [userId: string]: string;
@@ -1,8 +1,18 @@
1
+ import { ReactElement } from 'react';
1
2
  import { AvatarProps } from '@box/blueprint-web';
2
3
  import { ComboboxWithApiProps } from '@box/combobox-with-api';
3
- import { ReactElement } from 'react';
4
4
  import { CustomUserContactType, FetchedAvatarUrls, UserContactType, UserSelectorOption } from './types';
5
5
  export type BaseUserSelectorProps<T extends UserSelectorOption> = {
6
+ /**
7
+ * Default options to show when the input is empty
8
+ */
9
+ defaultOptions?: T[];
10
+ /**
11
+ * 0-based index (relative to the option list only) after which to insert a divider row.
12
+ * The option list includes selected options when they are shown in the dropdown.
13
+ * E.g. 1 = divider after the second option.
14
+ */
15
+ overlayDividerIndex?: number;
6
16
  /**
7
17
  * A dictionary with user ids mapped to avatar URLs
8
18
  */
@@ -11,10 +21,18 @@ export type BaseUserSelectorProps<T extends UserSelectorOption> = {
11
21
  * Function to fetch users when the input value changes
12
22
  */
13
23
  fetchUsers: (inputValue: string) => Promise<Array<T>>;
24
+ /**
25
+ * Label shown as the first row in the default options dropdown (e.g. "SUGGESTED").
26
+ */
27
+ overlayTitle?: string;
14
28
  /**
15
29
  * onChange for controlled selected users state
16
30
  */
17
31
  onSelectedUsersChange: (selectedUsers: T[]) => void;
32
+ /**
33
+ * Function to render the custom option
34
+ */
35
+ renderCustomOption?: (option: CustomUserContactType, renderAvatar: () => ReactElement<AvatarProps>) => React.ReactNode;
18
36
  /**
19
37
  * Controlled selected users state
20
38
  */
@@ -23,14 +41,6 @@ export type BaseUserSelectorProps<T extends UserSelectorOption> = {
23
41
  * Support "text area" like styling
24
42
  */
25
43
  variant?: 'input' | 'textarea';
26
- /**
27
- * Default options to show when the input is empty
28
- */
29
- defaultOptions?: T[];
30
- /**
31
- * Function to render the custom option
32
- */
33
- renderCustomOption?: (option: CustomUserContactType, renderAvatar: () => ReactElement<AvatarProps>) => React.ReactNode;
34
44
  };
35
45
  export type UserSelectorProps<T extends UserSelectorOption = UserContactType> = BaseUserSelectorProps<T> & Omit<ComboboxWithApiProps<true, false, T>, 'as' | 'fetcher' | 'freeInput' | 'hideSelectedOptions' | 'loadingAriaLabel' | 'multiselect' | 'onValueChange' | 'value'>;
36
46
  declare const UserSelector: <T extends UserSelectorOption = UserContactType>(props: UserSelectorProps<T>) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { OverlayHeaderOption, OverlaySeparatorOption, UserSelectorOption } from '../types';
2
+ /**
3
+ * Builds an options array with optional overlay header and divider for UserSelector.
4
+ * Used for both default options and fetcher results.
5
+ * Divider is inserted after the given index (relative to options only). Then overlay header is prepended.
6
+ * Returns the same reference when no overlay is needed or when options are undefined/empty.
7
+ */
8
+ export declare function buildOptionsWithOverlay<T extends UserSelectorOption>(options: T[] | undefined, overlayTitle: string, overlayDividerIndex: number): (T | OverlayHeaderOption | OverlaySeparatorOption)[] | undefined;
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@box/user-selector",
3
- "version": "1.68.2",
3
+ "version": "1.70.0",
4
4
  "peerDependencies": {
5
5
  "@box/blueprint-web": "^13.0.0",
6
6
  "@box/blueprint-web-assets": "^4.101.9",
7
- "@box/combobox-with-api": "^1.37.1",
7
+ "@box/combobox-with-api": "^1.38.0",
8
8
  "lodash": "^4.17.15",
9
9
  "react": "^17.0.0 || ^18.0.0",
10
10
  "react-dom": "^17.0.0 || ^18.0.0",
@@ -13,7 +13,7 @@
13
13
  "devDependencies": {
14
14
  "@box/blueprint-web": "^13.0.0",
15
15
  "@box/blueprint-web-assets": "^4.101.9",
16
- "@box/combobox-with-api": "^1.37.1",
16
+ "@box/combobox-with-api": "^1.38.0",
17
17
  "@box/storybook-utils": "^0.16.46",
18
18
  "@types/lodash": "^4.17.14",
19
19
  "react": "^18.3.0",