@activecollab/components 2.0.441 → 2.0.442

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 (47) hide show
  1. package/dist/cjs/presentation/shared/UserDetailsDialog.js +125 -0
  2. package/dist/cjs/presentation/shared/UserDetailsDialog.js.map +1 -0
  3. package/dist/cjs/presentation/shared/index.js +15 -0
  4. package/dist/cjs/presentation/shared/index.js.map +1 -1
  5. package/dist/cjs/presentation/whiteboard/bottomDock.js +10 -3
  6. package/dist/cjs/presentation/whiteboard/bottomDock.js.map +1 -1
  7. package/dist/cjs/presentation/whiteboard/canvasElements.js +114 -25
  8. package/dist/cjs/presentation/whiteboard/canvasElements.js.map +1 -1
  9. package/dist/cjs/presentation/whiteboard/entityCardActions.js +79 -0
  10. package/dist/cjs/presentation/whiteboard/entityCardActions.js.map +1 -0
  11. package/dist/cjs/presentation/whiteboard/inspector.js +20 -5
  12. package/dist/cjs/presentation/whiteboard/inspector.js.map +1 -1
  13. package/dist/cjs/presentation/whiteboard/outline.js +4 -2
  14. package/dist/cjs/presentation/whiteboard/outline.js.map +1 -1
  15. package/dist/cjs/presentation/whiteboard/projectElements.js +188 -53
  16. package/dist/cjs/presentation/whiteboard/projectElements.js.map +1 -1
  17. package/dist/esm/presentation/shared/UserDetailsDialog.d.ts +45 -0
  18. package/dist/esm/presentation/shared/UserDetailsDialog.d.ts.map +1 -0
  19. package/dist/esm/presentation/shared/UserDetailsDialog.js +115 -0
  20. package/dist/esm/presentation/shared/UserDetailsDialog.js.map +1 -0
  21. package/dist/esm/presentation/shared/index.d.ts +2 -0
  22. package/dist/esm/presentation/shared/index.d.ts.map +1 -1
  23. package/dist/esm/presentation/shared/index.js +1 -0
  24. package/dist/esm/presentation/shared/index.js.map +1 -1
  25. package/dist/esm/presentation/whiteboard/bottomDock.d.ts +3 -2
  26. package/dist/esm/presentation/whiteboard/bottomDock.d.ts.map +1 -1
  27. package/dist/esm/presentation/whiteboard/bottomDock.js +9 -4
  28. package/dist/esm/presentation/whiteboard/bottomDock.js.map +1 -1
  29. package/dist/esm/presentation/whiteboard/canvasElements.d.ts.map +1 -1
  30. package/dist/esm/presentation/whiteboard/canvasElements.js +94 -25
  31. package/dist/esm/presentation/whiteboard/canvasElements.js.map +1 -1
  32. package/dist/esm/presentation/whiteboard/entityCardActions.d.ts +37 -0
  33. package/dist/esm/presentation/whiteboard/entityCardActions.d.ts.map +1 -0
  34. package/dist/esm/presentation/whiteboard/entityCardActions.js +69 -0
  35. package/dist/esm/presentation/whiteboard/entityCardActions.js.map +1 -0
  36. package/dist/esm/presentation/whiteboard/inspector.d.ts.map +1 -1
  37. package/dist/esm/presentation/whiteboard/inspector.js +29 -7
  38. package/dist/esm/presentation/whiteboard/inspector.js.map +1 -1
  39. package/dist/esm/presentation/whiteboard/outline.d.ts +1 -1
  40. package/dist/esm/presentation/whiteboard/outline.d.ts.map +1 -1
  41. package/dist/esm/presentation/whiteboard/outline.js +5 -3
  42. package/dist/esm/presentation/whiteboard/outline.js.map +1 -1
  43. package/dist/esm/presentation/whiteboard/projectElements.d.ts +40 -8
  44. package/dist/esm/presentation/whiteboard/projectElements.d.ts.map +1 -1
  45. package/dist/esm/presentation/whiteboard/projectElements.js +181 -55
  46. package/dist/esm/presentation/whiteboard/projectElements.js.map +1 -1
  47. package/package.json +1 -1
@@ -3,7 +3,7 @@ import React, { useMemo, useRef, useState } from "react";
3
3
  import styled from "styled-components";
4
4
  import { SELECTION_BLUE, StyledCanvasElement } from "./elements";
5
5
  import { IconButton } from "../../components/IconButton";
6
- import { ArrowLeftIcon, SortGeneralIcon } from "../../components/Icons";
6
+ import { ArrowLeftIcon, PersonIcon, SortGeneralIcon } from "../../components/Icons";
7
7
  import { Input } from "../../components/Input";
8
8
  import { SkeletonLoader } from "../../components/Loaders";
9
9
  import { Modal } from "../../components/Modal";
@@ -12,7 +12,7 @@ import { RadioButton } from "../../components/RadioButton";
12
12
  import { StackedCard } from "../../components/StackedCard";
13
13
  import { Tooltip } from "../../components/Tooltip";
14
14
  import { Body2, Caption1 } from "../../components/Typography";
15
- import { AVATAR_COLORS } from "../shared";
15
+ import { AVATAR_COLORS, RoundAvatar } from "../shared";
16
16
  import { INTERNAL_CLIENT_LABEL as CONTENT_INTERNAL_CLIENT_LABEL, ProjectCard, TaskCard } from "../stackedCard/content";
17
17
 
18
18
  /* ------------------------------------------------------------------ */
@@ -54,8 +54,16 @@ export const TASK_CARD_WIDTH = 260;
54
54
 
55
55
  /* How long the "details are being resolved from the backend" flash lasts. */
56
56
  export const RESOLVE_MS = 480;
57
- /** True for the two board tools this module owns. */
58
- export const isProjectTool = tool => tool === "project" || tool === "task";
57
+ /** True for the three board tools this module owns. */
58
+ export const isProjectTool = tool => tool === "project" || tool === "task" || tool === "person";
59
+
60
+ /* A person is not a card: a square whose width IS its height, so the avatar
61
+ circle stays a circle. The drawn width is its side, clamped to these bounds —
62
+ the same 1:1 lock the app enforces on the canvas grip and in the Inspector. */
63
+ export const PERSON_DEFAULT_SIZE = 96;
64
+ export const PERSON_MIN_SIZE = 48;
65
+ export const PERSON_MAX_SIZE = 240;
66
+ export const clampPersonSize = size => Math.min(PERSON_MAX_SIZE, Math.max(PERSON_MIN_SIZE, Math.round(size)));
59
67
 
60
68
  /* ------------------------------------------------------------------ */
61
69
  /* Mock data */
@@ -119,6 +127,18 @@ export const PICKER_PEOPLE = {
119
127
  }
120
128
  };
121
129
 
130
+ /* The People picker's one flat list: everyone the board knows, by name, with
131
+ no company or project layer — a person chip points at a user, not at a
132
+ membership. The record key doubles as the row id. */
133
+ export const PICKER_PEOPLE_LIST = Object.entries(PICKER_PEOPLE).map(_ref => {
134
+ let id = _ref[0],
135
+ person = _ref[1];
136
+ return {
137
+ id,
138
+ person
139
+ };
140
+ }).sort((a, b) => a.person.name.localeCompare(b.person.name));
141
+
122
142
  /* Label colours are data too: they are the label's own colour in the app. */
123
143
  export const PICKER_LABELS = {
124
144
  assigned: {
@@ -419,6 +439,10 @@ export const projectElementTitle = element => {
419
439
  if (element.limited) {
420
440
  return LIMITED_ACCESS_LABEL;
421
441
  }
442
+ if (element.kind === "person") {
443
+ var _element$person$name, _element$person;
444
+ return (_element$person$name = (_element$person = element.person) == null ? void 0 : _element$person.name) != null ? _element$person$name : "Person";
445
+ }
422
446
  if (element.kind === "project") {
423
447
  var _element$project$name, _element$project;
424
448
  return (_element$project$name = (_element$project = element.project) == null ? void 0 : _element$project.name) != null ? _element$project$name : "Project";
@@ -452,7 +476,8 @@ const SEEDED = [{
452
476
  status: "resolved",
453
477
  limited: true,
454
478
  project: null,
455
- task: null
479
+ task: null,
480
+ person: null
456
481
  },
457
482
  x: 120,
458
483
  y: 200
@@ -465,7 +490,8 @@ const SEEDED = [{
465
490
  status: "resolved",
466
491
  limited: true,
467
492
  project: null,
468
- task: null
493
+ task: null,
494
+ person: null
469
495
  },
470
496
  x: 120,
471
497
  y: 380
@@ -489,7 +515,7 @@ export const SEEDED_PROJECT_ELEMENT_POSITIONS = SEEDED.reduce((acc, seed) => {
489
515
  export const StyledProjectElementBox = styled(StyledCanvasElement).withConfig({
490
516
  displayName: "projectElements__StyledProjectElementBox",
491
517
  componentId: "sc-1cayxi5-0"
492
- })(["border-radius:8px;transition:width 160ms ease;&.limited{cursor:not-allowed;}"]);
518
+ })(["border-radius:8px;transition:width 160ms ease;&.limited{cursor:not-allowed;}&.person{border-radius:50%;}"]);
493
519
 
494
520
  /* The rubber band and the armed tool's hover preview — the same blue dashed
495
521
  box the link tool draws, so the gesture reads as one board convention. */
@@ -498,18 +524,44 @@ export const StyledProjectDraft = styled.div.withConfig({
498
524
  componentId: "sc-1cayxi5-1"
499
525
  })(["position:absolute;box-sizing:border-box;border:1.5px dashed ", ";border-radius:8px;background-color:rgba(59,130,246,0.08);pointer-events:none;"], SELECTION_BLUE);
500
526
 
527
+ /* The person rubber band and hover preview: the same dashed blue, round. */
528
+ export const StyledPersonDraft = styled(StyledProjectDraft).withConfig({
529
+ displayName: "projectElements__StyledPersonDraft",
530
+ componentId: "sc-1cayxi5-2"
531
+ })(["border-radius:50%;"]);
532
+
501
533
  /* The placeholder that waits for the picker's answer. Card-like on purpose:
502
534
  rounded, dashed, tinted — a card-shaped hole, not a selection marquee. */
503
535
  export const StyledPlaceholder = styled.div.withConfig({
504
536
  displayName: "projectElements__StyledPlaceholder",
505
- componentId: "sc-1cayxi5-2"
537
+ componentId: "sc-1cayxi5-3"
506
538
  })(["box-sizing:border-box;width:100%;height:100%;display:flex;align-items:center;justify-content:center;padding:12px;border:1.5px dashed var(--border-primary);border-radius:8px;background-color:var(--color-theme-200);user-select:none;"]);
507
539
 
540
+ /* A person's placeholder is the circle its avatar will fill — dashed and
541
+ tinted like the card placeholder, with the person glyph where the face will
542
+ be, so the pick swaps one circle for another without a change of shape. */
543
+ export const StyledPersonPlaceholder = styled(StyledPlaceholder).withConfig({
544
+ displayName: "projectElements__StyledPersonPlaceholder",
545
+ componentId: "sc-1cayxi5-4"
546
+ })(["border-radius:50%;padding:0;color:var(--color-theme-600);svg{width:40%;height:40%;fill:currentColor;}"]);
547
+ export const StyledPersonResolving = styled.div.withConfig({
548
+ displayName: "projectElements__StyledPersonResolving",
549
+ componentId: "sc-1cayxi5-5"
550
+ })(["width:100%;height:100%;border-radius:50%;overflow:hidden;.c-loader{width:100%;height:100%;border-radius:50%;}"]);
551
+
552
+ /* The resolved chip. The avatar fills the element's square and the name hangs
553
+ below it, outside the box: the circle is the whole footprint the board
554
+ measures, so the caption never changes the element's size. */
555
+ export const StyledPersonFace = styled.div.withConfig({
556
+ displayName: "projectElements__StyledPersonFace",
557
+ componentId: "sc-1cayxi5-6"
558
+ })(["position:relative;width:100%;height:100%;border-radius:50%;user-select:none;.person-avatar{width:100%;height:100%;}.person-name{position:absolute;top:100%;left:50%;transform:translateX(-50%);margin-top:6px;white-space:nowrap;}"]);
559
+
508
560
  /* The resolving flash: the placeholder's box, with the card's rows sketched in
509
561
  so the swap to the real card does not read as a different element. */
510
562
  export const StyledResolving = styled(StyledPlaceholder).withConfig({
511
563
  displayName: "projectElements__StyledResolving",
512
- componentId: "sc-1cayxi5-3"
564
+ componentId: "sc-1cayxi5-7"
513
565
  })(["flex-direction:column;align-items:stretch;justify-content:center;gap:10px;.c-loader{height:12px;}"]);
514
566
 
515
567
  /**
@@ -531,8 +583,8 @@ export const StyledResolving = styled(StyledPlaceholder).withConfig({
531
583
  */
532
584
  export const StyledLimitedCard = styled.div.withConfig({
533
585
  displayName: "projectElements__StyledLimitedCard",
534
- componentId: "sc-1cayxi5-4"
535
- })(["box-sizing:border-box;width:100%;height:100%;min-height:96px;display:flex;align-items:center;justify-content:center;padding:12px;border:1px solid var(--border-primary);border-radius:8px;background-color:var(--muted-surface);background-image:repeating-linear-gradient( -45deg,transparent,transparent 5px,var(--stripe-pattern) 5px,var(--stripe-pattern) 5.7px );color:var(--muted-surface-text);cursor:not-allowed;user-select:none;.limited-label{color:var(--muted-surface-text);}"]);
586
+ componentId: "sc-1cayxi5-8"
587
+ })(["box-sizing:border-box;width:100%;height:100%;min-height:96px;display:flex;align-items:center;justify-content:center;padding:12px;border:1px solid var(--border-primary);border-radius:8px;background-color:var(--muted-surface);background-image:repeating-linear-gradient( -45deg,transparent,transparent 5px,var(--stripe-pattern) 5px,var(--stripe-pattern) 5.7px );color:var(--muted-surface-text);cursor:not-allowed;user-select:none;.limited-label{color:var(--muted-surface-text);}&.round{border-radius:50%;min-height:0;}"]);
536
588
 
537
589
  /**
538
590
  * Both resolved faces are the design system's `StackedCard` — the same shell the
@@ -545,7 +597,7 @@ export const StyledLimitedCard = styled.div.withConfig({
545
597
  */
546
598
  const StyledEntityCard = styled(StackedCard).withConfig({
547
599
  displayName: "projectElements__StyledEntityCard",
548
- componentId: "sc-1cayxi5-5"
600
+ componentId: "sc-1cayxi5-9"
549
601
  })(["&&{width:100%;}user-select:none;"]);
550
602
  /**
551
603
  * The task face — the shared `TaskCard`, with the project line switched on.
@@ -560,11 +612,11 @@ const StyledEntityCard = styled(StackedCard).withConfig({
560
612
  * The title is the app's own display name (`#123: Name`, `taskDisplayName`), so
561
613
  * the card's separate task-number prefix stays off.
562
614
  */
563
- export const TaskElementCard = _ref => {
615
+ export const TaskElementCard = _ref2 => {
564
616
  var _task$subtasks, _task$comments, _task$date;
565
- let project = _ref.project,
566
- task = _ref.task,
567
- selected = _ref.selected;
617
+ let project = _ref2.project,
618
+ task = _ref2.task,
619
+ selected = _ref2.selected;
568
620
  return /*#__PURE__*/React.createElement(StyledEntityCard, {
569
621
  selected: selected
570
622
  }, /*#__PURE__*/React.createElement(TaskCard, {
@@ -589,9 +641,9 @@ export const TaskElementCard = _ref => {
589
641
  * right. No progress indicator, and no row at all when the project has neither a
590
642
  * leader nor a label.
591
643
  */
592
- export const ProjectElementCard = _ref2 => {
593
- let project = _ref2.project,
594
- selected = _ref2.selected;
644
+ export const ProjectElementCard = _ref3 => {
645
+ let project = _ref3.project,
646
+ selected = _ref3.selected;
595
647
  return /*#__PURE__*/React.createElement(StyledEntityCard, {
596
648
  selected: selected
597
649
  }, /*#__PURE__*/React.createElement(ProjectCard, {
@@ -617,23 +669,74 @@ export const ProjectElementCard = _ref2 => {
617
669
  * not the entity's identity — the opposite of the tooltip Workload disables on
618
670
  * a limited event, which would have leaked the name.
619
671
  */
620
- export const LimitedElementCard = () => /*#__PURE__*/React.createElement(Tooltip, {
621
- title: LIMITED_ACCESS_MESSAGE
622
- }, /*#__PURE__*/React.createElement(StyledLimitedCard, {
623
- "data-testid": "wb-limited-card"
624
- }, /*#__PURE__*/React.createElement(Caption1, {
625
- weight: "bold",
626
- className: "limited-label"
627
- }, LIMITED_ACCESS_LABEL)));
672
+ export const LimitedElementCard = _ref4 => {
673
+ let _ref4$round = _ref4.round,
674
+ round = _ref4$round === void 0 ? false : _ref4$round;
675
+ return /*#__PURE__*/React.createElement(Tooltip, {
676
+ title: LIMITED_ACCESS_MESSAGE
677
+ }, /*#__PURE__*/React.createElement(StyledLimitedCard, {
678
+ "data-testid": "wb-limited-card",
679
+ className: round ? "round" : undefined
680
+ }, /*#__PURE__*/React.createElement(Caption1, {
681
+ weight: "bold",
682
+ className: "limited-label"
683
+ }, LIMITED_ACCESS_LABEL)));
684
+ };
685
+ /**
686
+ * The person face — an avatar circle with a name caption. Always a chip: never
687
+ * a StackedCard, it never expands, no hover card, no online-status dot. The
688
+ * initials avatar stands in for the server-rendered avatar, as it does on the
689
+ * cards.
690
+ */
691
+ export const PersonElementFace = _ref5 => {
692
+ let person = _ref5.person,
693
+ size = _ref5.size;
694
+ return /*#__PURE__*/React.createElement(StyledPersonFace, {
695
+ "data-testid": "wb-person-face"
696
+ }, /*#__PURE__*/React.createElement(RoundAvatar, {
697
+ className: "person-avatar",
698
+ $bg: person.color,
699
+ $size: size,
700
+ $bordered: false,
701
+ style: {
702
+ fontSize: Math.round(size * 0.28)
703
+ },
704
+ "aria-label": person.name
705
+ }, person.initials), /*#__PURE__*/React.createElement(Caption1, {
706
+ weight: "bold",
707
+ className: "person-name"
708
+ }, person.name));
709
+ };
628
710
 
629
711
  /** One board element, in whichever of its three states it currently is. */
630
- export const ProjectElementNode = _ref3 => {
631
- let element = _ref3.element,
632
- selected = _ref3.selected;
712
+ export const ProjectElementNode = _ref6 => {
713
+ let element = _ref6.element,
714
+ selected = _ref6.selected;
633
715
  /* Checked before anything else: a limited element must never be able to fall
634
716
  through to a branch that renders entity data. */
635
717
  if (element.limited) {
636
- return /*#__PURE__*/React.createElement(LimitedElementCard, null);
718
+ return /*#__PURE__*/React.createElement(LimitedElementCard, {
719
+ round: element.kind === "person"
720
+ });
721
+ }
722
+
723
+ /* A person is the board's second non-card render: every state is a circle. */
724
+ if (element.kind === "person") {
725
+ if (element.status === "placeholder") {
726
+ return /*#__PURE__*/React.createElement(StyledPersonPlaceholder, {
727
+ "aria-label": "Pick a person"
728
+ }, /*#__PURE__*/React.createElement(PersonIcon, null));
729
+ }
730
+ if (element.status === "resolving") {
731
+ return /*#__PURE__*/React.createElement(StyledPersonResolving, null, /*#__PURE__*/React.createElement(SkeletonLoader, null));
732
+ }
733
+ if (element.person) {
734
+ return /*#__PURE__*/React.createElement(PersonElementFace, {
735
+ person: element.person,
736
+ size: element.width
737
+ });
738
+ }
739
+ return /*#__PURE__*/React.createElement(StyledPersonPlaceholder, null);
637
740
  }
638
741
  if (element.status === "placeholder") {
639
742
  return /*#__PURE__*/React.createElement(StyledPlaceholder, null, /*#__PURE__*/React.createElement(Caption1, {
@@ -681,30 +784,31 @@ export const ProjectElementNode = _ref3 => {
681
784
  share: the portal, the overlay, the focus lock and the Esc handler. */
682
785
  const StyledPickerPanel = styled(Paper).withConfig({
683
786
  displayName: "projectElements__StyledPickerPanel",
684
- componentId: "sc-1cayxi5-6"
787
+ componentId: "sc-1cayxi5-10"
685
788
  })(["position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:flex;flex-direction:column;width:470px;max-width:calc(100vw - 32px);max-height:70vh;overflow:hidden;&&{border-radius:12px;border:1px solid var(--border-primary);box-shadow:0 18px 48px rgba(0,0,0,0.24);}"]);
686
789
 
687
790
  /* Search + adornments, mirroring `Select`'s own form row: the back arrow is a
688
791
  start adornment (TaskList), the group-by sort a end adornment (ProjectList). */
689
792
  const StyledPickerHeader = styled.div.withConfig({
690
793
  displayName: "projectElements__StyledPickerHeader",
691
- componentId: "sc-1cayxi5-7"
794
+ componentId: "sc-1cayxi5-11"
692
795
  })(["display:flex;align-items:center;gap:8px;flex-shrink:0;padding:12px 12px 8px;.picker-search{width:100%;border-radius:8px;}.picker-search:focus-within{border-color:var(--color-primary);outline:1px solid var(--color-primary);}"]);
693
796
  const StyledPickerList = styled.div.withConfig({
694
797
  displayName: "projectElements__StyledPickerList",
695
- componentId: "sc-1cayxi5-8"
798
+ componentId: "sc-1cayxi5-12"
696
799
  })(["flex:1 1 auto;min-height:0;overflow-y:auto;padding:4px 0 12px;.picker-group-header{box-sizing:border-box;height:36px;display:flex;align-items:center;padding:0 20px;margin-top:4px;}.picker-empty{padding:24px 20px;text-align:center;}"]);
697
800
 
698
801
  /* A generously spaced row: the name on the left, an empty radio on the right —
699
802
  the shape `Select` gives a single-choice option, at picker scale. */
700
803
  const StyledPickerRow = styled.div.withConfig({
701
804
  displayName: "projectElements__StyledPickerRow",
702
- componentId: "sc-1cayxi5-9"
703
- })(["box-sizing:border-box;height:56px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 20px;cursor:pointer;outline:none;&:hover,&:focus-visible{background-color:var(--color-theme-200);}.row-name{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}.c-radio-btn{flex-shrink:0;}"]);
704
- const PickerRow = _ref4 => {
705
- let id = _ref4.id,
706
- name = _ref4.name,
707
- onSelect = _ref4.onSelect;
805
+ componentId: "sc-1cayxi5-13"
806
+ })(["box-sizing:border-box;height:56px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 20px;cursor:pointer;outline:none;&:hover,&:focus-visible{background-color:var(--color-theme-200);}.row-lead{display:flex;align-items:center;gap:12px;min-width:0;}.row-name{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}.c-radio-btn{flex-shrink:0;}"]);
807
+ const PickerRow = _ref7 => {
808
+ let id = _ref7.id,
809
+ name = _ref7.name,
810
+ avatar = _ref7.avatar,
811
+ onSelect = _ref7.onSelect;
708
812
  return /*#__PURE__*/React.createElement(StyledPickerRow, {
709
813
  role: "button",
710
814
  tabIndex: 0,
@@ -716,23 +820,31 @@ const PickerRow = _ref4 => {
716
820
  }
717
821
  }
718
822
  }, /*#__PURE__*/React.createElement("span", {
823
+ className: "row-lead"
824
+ }, avatar ? /*#__PURE__*/React.createElement(RoundAvatar, {
825
+ $bg: avatar.color,
826
+ $size: 24,
827
+ $bordered: false
828
+ }, avatar.initials) : null, /*#__PURE__*/React.createElement("span", {
719
829
  className: "row-name"
720
- }, /*#__PURE__*/React.createElement(Body2, null, name)), /*#__PURE__*/React.createElement(RadioButton, {
830
+ }, /*#__PURE__*/React.createElement(Body2, null, name))), /*#__PURE__*/React.createElement(RadioButton, {
721
831
  id: "wb-picker-" + id,
722
832
  tabIndex: -1
723
833
  }));
724
834
  };
725
835
  /**
726
- * One picker, two flows — the same split the app makes between
727
- * `MenuSelector/ProjectList` (one step) and `ProjectTaskMenuSelector` (project,
728
- * then task). The task flow deliberately drops the app's "Directly on project"
729
- * default option: a task card has to be a task.
836
+ * One picker, three flows — the same split the app makes between
837
+ * `MenuSelector/ProjectList` (one step), `ProjectTaskMenuSelector` (project,
838
+ * then task) and the flat People list (one step, no grouping). The task flow
839
+ * deliberately drops the app's "Directly on project" default option: a task
840
+ * card has to be a task.
730
841
  */
731
- export const ProjectElementPicker = _ref5 => {
732
- let kind = _ref5.kind,
733
- onSelectProject = _ref5.onSelectProject,
734
- onSelectTask = _ref5.onSelectTask,
735
- onCancel = _ref5.onCancel;
842
+ export const ProjectElementPicker = _ref8 => {
843
+ let kind = _ref8.kind,
844
+ onSelectProject = _ref8.onSelectProject,
845
+ onSelectTask = _ref8.onSelectTask,
846
+ onSelectPerson = _ref8.onSelectPerson,
847
+ onCancel = _ref8.onCancel;
736
848
  const _useState = useState(null),
737
849
  project = _useState[0],
738
850
  setProject = _useState[1];
@@ -746,6 +858,7 @@ export const ProjectElementPicker = _ref5 => {
746
858
  gesture that created it. */
747
859
  const pressedInsideRef = useRef(false);
748
860
  const onTaskStep = kind === "task" && project !== null;
861
+ const onPeopleStep = kind === "person";
749
862
  const projectGroups = useMemo(() => groupProjects(PICKER_PROJECTS.filter(entry => matchesQuery(entry.name, query))), [query]);
750
863
  const taskGroups = useMemo(() => project ? groupTasks(project.tasks.filter(task => matchesQuery(taskDisplayName(task), query))) : [], [project, query]);
751
864
  const handleProject = picked => {
@@ -760,7 +873,10 @@ export const ProjectElementPicker = _ref5 => {
760
873
  setProject(null);
761
874
  setQuery("");
762
875
  };
763
- const isEmpty = onTaskStep ? taskGroups.length === 0 : projectGroups.length === 0;
876
+ const people = useMemo(() => PICKER_PEOPLE_LIST.filter(entry => matchesQuery(entry.person.name, query)), [query]);
877
+ const isEmpty = onPeopleStep ? people.length === 0 : onTaskStep ? taskGroups.length === 0 : projectGroups.length === 0;
878
+ const searchPlaceholder = onPeopleStep ? "Search a person" : onTaskStep ? "Search a task" : "Search a project";
879
+ const emptyLabel = onPeopleStep ? "No people" : onTaskStep ? "No Result" : "No Projects";
764
880
  return /*#__PURE__*/React.createElement(Modal, {
765
881
  open: true,
766
882
  onClose: onCancel,
@@ -786,9 +902,9 @@ export const ProjectElementPicker = _ref5 => {
786
902
  className: "picker-search",
787
903
  autoFocus: true,
788
904
  value: query,
789
- placeholder: onTaskStep ? "Search a task" : "Search a project",
905
+ placeholder: searchPlaceholder,
790
906
  onChange: event => setQuery(event.target.value)
791
- }), onTaskStep ? null : /*#__PURE__*/React.createElement(Tooltip, {
907
+ }), onTaskStep || onPeopleStep ? null : /*#__PURE__*/React.createElement(Tooltip, {
792
908
  title: "Group by"
793
909
  }, /*#__PURE__*/React.createElement(IconButton, {
794
910
  type: "button",
@@ -797,7 +913,17 @@ export const ProjectElementPicker = _ref5 => {
797
913
  className: "picker-empty"
798
914
  }, /*#__PURE__*/React.createElement(Body2, {
799
915
  color: "tertiary"
800
- }, onTaskStep ? "No Result" : "No Projects")) : null, onTaskStep && project ? taskGroups.map(group => /*#__PURE__*/React.createElement("div", {
916
+ }, emptyLabel)) : null, onPeopleStep ? people.map(_ref9 => {
917
+ let id = _ref9.id,
918
+ person = _ref9.person;
919
+ return /*#__PURE__*/React.createElement(PickerRow, {
920
+ key: id,
921
+ id: id,
922
+ name: person.name,
923
+ avatar: person,
924
+ onSelect: () => onSelectPerson(person)
925
+ });
926
+ }) : null, onTaskStep && project ? taskGroups.map(group => /*#__PURE__*/React.createElement("div", {
801
927
  key: group.taskList
802
928
  }, /*#__PURE__*/React.createElement("div", {
803
929
  className: "picker-group-header"
@@ -808,7 +934,7 @@ export const ProjectElementPicker = _ref5 => {
808
934
  id: task.id,
809
935
  name: taskDisplayName(task),
810
936
  onSelect: () => onSelectTask(project, task)
811
- })))) : null, onTaskStep ? null : projectGroups.map(group => /*#__PURE__*/React.createElement("div", {
937
+ })))) : null, onTaskStep || onPeopleStep ? null : projectGroups.map(group => /*#__PURE__*/React.createElement("div", {
812
938
  key: group.client
813
939
  }, /*#__PURE__*/React.createElement("div", {
814
940
  className: "picker-group-header"