@box/user-selector 1.68.1 → 1.69.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 +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,20 +1,20 @@
1
1
  {
2
2
  "name": "@box/user-selector",
3
- "version": "1.68.1",
3
+ "version": "1.69.0",
4
4
  "peerDependencies": {
5
- "@box/blueprint-web": "^12.139.0",
6
- "@box/blueprint-web-assets": "^4.101.8",
7
- "@box/combobox-with-api": "^1.37.0",
5
+ "@box/blueprint-web": "^13.0.0",
6
+ "@box/blueprint-web-assets": "^4.101.9",
7
+ "@box/combobox-with-api": "^1.37.1",
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",
11
11
  "react-intl": "6.4.2"
12
12
  },
13
13
  "devDependencies": {
14
- "@box/blueprint-web": "^12.139.0",
15
- "@box/blueprint-web-assets": "^4.101.8",
16
- "@box/combobox-with-api": "^1.37.0",
17
- "@box/storybook-utils": "^0.16.45",
14
+ "@box/blueprint-web": "^13.0.0",
15
+ "@box/blueprint-web-assets": "^4.101.9",
16
+ "@box/combobox-with-api": "^1.37.1",
17
+ "@box/storybook-utils": "^0.16.46",
18
18
  "@types/lodash": "^4.17.14",
19
19
  "react": "^18.3.0",
20
20
  "react-dom": "^18.3.0",