@dxos/react-ui-list 0.7.1 → 0.7.2-main.f1adc9f

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 (42) hide show
  1. package/dist/lib/browser/index.mjs +149 -87
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +180 -116
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +149 -87
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/components/List/DropIndicator.d.ts +5 -4
  11. package/dist/types/src/components/List/DropIndicator.d.ts.map +1 -1
  12. package/dist/types/src/components/Tree/DropIndicator.d.ts +2 -1
  13. package/dist/types/src/components/Tree/DropIndicator.d.ts.map +1 -1
  14. package/dist/types/src/components/Tree/Tree.d.ts +4 -19
  15. package/dist/types/src/components/Tree/Tree.d.ts.map +1 -1
  16. package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -1
  17. package/dist/types/src/components/Tree/TreeContext.d.ts +20 -0
  18. package/dist/types/src/components/Tree/TreeContext.d.ts.map +1 -0
  19. package/dist/types/src/components/Tree/TreeItem.d.ts +26 -24
  20. package/dist/types/src/components/Tree/TreeItem.d.ts.map +1 -1
  21. package/dist/types/src/components/Tree/TreeItemHeading.d.ts +1 -1
  22. package/dist/types/src/components/Tree/TreeItemHeading.d.ts.map +1 -1
  23. package/dist/types/src/components/Tree/helpers.d.ts +0 -3
  24. package/dist/types/src/components/Tree/helpers.d.ts.map +1 -1
  25. package/dist/types/src/components/Tree/index.d.ts +1 -1
  26. package/dist/types/src/components/Tree/index.d.ts.map +1 -1
  27. package/dist/types/src/components/Tree/testing.d.ts +3 -6
  28. package/dist/types/src/components/Tree/testing.d.ts.map +1 -1
  29. package/package.json +16 -18
  30. package/src/components/List/DropIndicator.tsx +7 -2
  31. package/src/components/Tree/DropIndicator.tsx +7 -8
  32. package/src/components/Tree/Tree.stories.tsx +71 -70
  33. package/src/components/Tree/Tree.tsx +29 -24
  34. package/src/components/Tree/TreeContext.tsx +32 -0
  35. package/src/components/Tree/TreeItem.tsx +122 -83
  36. package/src/components/Tree/TreeItemHeading.tsx +14 -5
  37. package/src/components/Tree/helpers.ts +0 -16
  38. package/src/components/Tree/index.ts +1 -1
  39. package/src/components/Tree/testing.ts +4 -73
  40. package/dist/types/src/components/Tree/types.d.ts +0 -18
  41. package/dist/types/src/components/Tree/types.d.ts.map +0 -1
  42. package/src/components/Tree/types.ts +0 -34
@@ -32,8 +32,8 @@ var edgeStyles = {
32
32
  var strokeSize = 2;
33
33
  var terminalSize = 8;
34
34
  var offsetToAlignTerminalWithLine = (strokeSize - terminalSize) / 2;
35
- var DropIndicator = ({ edge, gap: gap2 = "0px" }) => {
36
- const lineOffset = `calc(-0.5 * (${gap2} + ${strokeSize}px))`;
35
+ var DropIndicator = ({ edge, gap = 0 }) => {
36
+ const lineOffset = `calc(-0.5 * (${gap}px + ${strokeSize}px))`;
37
37
  const orientation = edgeToOrientationMap[edge];
38
38
  return /* @__PURE__ */ React.createElement("div", {
39
39
  style: {
@@ -57,7 +57,7 @@ import React2, { useCallback, useEffect, useState } from "react";
57
57
  var LIST_NAME = "List";
58
58
  var [ListProvider, useListContext] = createContext(LIST_NAME);
59
59
  var defaultGetId = (item) => item?.id;
60
- var ListRoot = ({ classNames, children, items, isItem: isItem2, getId = defaultGetId, onMove, ...props }) => {
60
+ var ListRoot = ({ classNames, children, items, isItem, getId = defaultGetId, onMove, ...props }) => {
61
61
  const isEqual = useCallback((a, b) => {
62
62
  const idA = getId?.(a);
63
63
  const idB = getId?.(b);
@@ -75,7 +75,7 @@ var ListRoot = ({ classNames, children, items, isItem: isItem2, getId = defaultG
75
75
  return;
76
76
  }
77
77
  return monitorForElements({
78
- canMonitor: ({ source }) => isItem2(source.data),
78
+ canMonitor: ({ source }) => isItem(source.data),
79
79
  onDrop: ({ location, source }) => {
80
80
  const target = location.current.dropTargets[0];
81
81
  if (!target) {
@@ -83,7 +83,7 @@ var ListRoot = ({ classNames, children, items, isItem: isItem2, getId = defaultG
83
83
  }
84
84
  const sourceData = source.data;
85
85
  const targetData = target.data;
86
- if (!isItem2(sourceData) || !isItem2(targetData)) {
86
+ if (!isItem(sourceData) || !isItem(targetData)) {
87
87
  return;
88
88
  }
89
89
  const sourceIdx = items.findIndex((item) => isEqual(item, sourceData));
@@ -107,7 +107,7 @@ var ListRoot = ({ classNames, children, items, isItem: isItem2, getId = defaultG
107
107
  onMove
108
108
  ]);
109
109
  return /* @__PURE__ */ React2.createElement(ListProvider, {
110
- isItem: isItem2,
110
+ isItem,
111
111
  state,
112
112
  setState,
113
113
  ...props
@@ -129,7 +129,7 @@ var defaultContext = {};
129
129
  var LIST_ITEM_NAME = "ListItem";
130
130
  var [ListItemProvider, useListItemContext] = createContext2(LIST_ITEM_NAME, defaultContext);
131
131
  var ListItem = ({ children, classNames, item }) => {
132
- const { isItem: isItem2, dragPreview, setState: setRootState } = useListContext(LIST_ITEM_NAME);
132
+ const { isItem, dragPreview, setState: setRootState } = useListContext(LIST_ITEM_NAME);
133
133
  const ref = useRef(null);
134
134
  const dragHandleRef = useRef(null);
135
135
  const [state, setState] = useState2(idle);
@@ -197,7 +197,7 @@ var ListItem = ({ children, classNames, item }) => {
197
197
  dropTargetForElements({
198
198
  element,
199
199
  canDrop: ({ source }) => {
200
- return source.element !== element && isItem2(source.data);
200
+ return source.element !== element && isItem(source.data);
201
201
  },
202
202
  getData: ({ input }) => {
203
203
  return attachClosestEdge(item, {
@@ -311,15 +311,22 @@ var List = {
311
311
  };
312
312
 
313
313
  // packages/ui/react-ui-list/src/components/Tree/Tree.tsx
314
- import React8 from "react";
314
+ import React8, { useMemo as useMemo2 } from "react";
315
315
  import { Treegrid as Treegrid2 } from "@dxos/react-ui";
316
- import { Path } from "@dxos/react-ui-mosaic";
316
+
317
+ // packages/ui/react-ui-list/src/components/Tree/TreeContext.tsx
318
+ import { createContext as createContext3, useContext } from "react";
319
+ import { raise } from "@dxos/debug";
320
+ var TreeContext = /* @__PURE__ */ createContext3(null);
321
+ var useTree = () => useContext(TreeContext) ?? raise(new Error("TreeContext not found"));
322
+ var TreeProvider = TreeContext.Provider;
317
323
 
318
324
  // packages/ui/react-ui-list/src/components/Tree/TreeItem.tsx
319
325
  import { combine as combine2 } from "@atlaskit/pragmatic-drag-and-drop/combine";
320
- import { draggable as draggable2, dropTargetForElements as dropTargetForElements2 } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
326
+ import { draggable as pragmaticDraggable, dropTargetForElements as dropTargetForElements2 } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
321
327
  import { attachInstruction, extractInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item";
322
- import React7, { memo as memo3, useCallback as useCallback3, useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
328
+ import React7, { memo as memo3, useCallback as useCallback3, useEffect as useEffect3, useMemo, useRef as useRef2, useState as useState3 } from "react";
329
+ import { S } from "@dxos/echo-schema";
323
330
  import { invariant as invariant2 } from "@dxos/invariant";
324
331
  import { Treegrid } from "@dxos/react-ui";
325
332
  import { focusRing, ghostHover, hoverableControls, hoverableFocusedKeyboardControls, hoverableFocusedWithinControls, mx as mx6 } from "@dxos/react-ui-theme";
@@ -348,9 +355,8 @@ var instructionStyles = {
348
355
  var strokeSize2 = 2;
349
356
  var terminalSize2 = 8;
350
357
  var offsetToAlignTerminalWithLine2 = (strokeSize2 - terminalSize2) / 2;
351
- var gap = "0px";
352
- var DropIndicator2 = ({ instruction }) => {
353
- const lineOffset = `calc(-0.5 * (${gap} + ${strokeSize2}px))`;
358
+ var DropIndicator2 = ({ instruction, gap = 0 }) => {
359
+ const lineOffset = `calc(-0.5 * (${gap}px + ${strokeSize2}px))`;
354
360
  const isBlocked = instruction.type === "instruction-blocked";
355
361
  const desiredInstruction = isBlocked ? instruction.desired : instruction;
356
362
  const orientation = edgeToOrientationMap2[desiredInstruction.type];
@@ -378,11 +384,16 @@ import { TextTooltip } from "@dxos/react-ui-text-tooltip";
378
384
  import { mx as mx4 } from "@dxos/react-ui-theme";
379
385
  var TreeItemHeading = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef2(({ label, icon, className, disabled, current, onSelect }, forwardedRef) => {
380
386
  const { t } = useTranslation();
387
+ const handleSelect = useCallback2((event) => {
388
+ onSelect?.(event.altKey);
389
+ }, [
390
+ onSelect
391
+ ]);
381
392
  const handleButtonKeydown = useCallback2((event) => {
382
393
  if (event.key === " " || event.key === "Enter") {
383
394
  event.preventDefault();
384
395
  event.stopPropagation();
385
- onSelect?.();
396
+ onSelect?.(event.altKey);
386
397
  }
387
398
  }, [
388
399
  onSelect
@@ -400,7 +411,7 @@ var TreeItemHeading = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef2(({ label,
400
411
  density: "fine",
401
412
  classNames: mx4("grow gap-2 !pis-0.5 hover:!bg-transparent dark:hover:!bg-transparent", "disabled:!cursor-default disabled:!opacity-100", className),
402
413
  disabled,
403
- onClick: onSelect,
414
+ onClick: handleSelect,
404
415
  onKeyDown: handleButtonKeydown,
405
416
  ...current && {
406
417
  "aria-current": "location"
@@ -410,7 +421,8 @@ var TreeItemHeading = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef2(({ label,
410
421
  size: 4,
411
422
  classNames: "is-[1em] bs-[1em] mlb-1"
412
423
  }), /* @__PURE__ */ React5.createElement("span", {
413
- className: "flex-1 is-0 truncate text-start text-sm font-normal"
424
+ className: "flex-1 is-0 truncate text-start text-sm font-normal",
425
+ "data-tooltip": true
414
426
  }, toLocalizedString(label, t))));
415
427
  }));
416
428
 
@@ -439,25 +451,41 @@ var DEFAULT_INDENTATION = 8;
439
451
  var paddingIndendation = (level, indentation = DEFAULT_INDENTATION) => ({
440
452
  paddingInlineStart: `${(level - 1) * indentation}px`
441
453
  });
442
- var getMode = (items, index) => {
443
- const item = items[index];
444
- const next = items[index + 1];
445
- if (!next || item.path.length > next.path.length) {
446
- return "last-in-group";
447
- } else if (item.path.length < next.path.length) {
448
- return "expanded";
449
- } else {
450
- return "standard";
451
- }
452
- };
453
454
 
454
455
  // packages/ui/react-ui-list/src/components/Tree/TreeItem.tsx
455
456
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-list/src/components/Tree/TreeItem.tsx";
456
457
  var hoverableDescriptionIcons = "[--icons-color:inherit] hover-hover:[--icons-color:var(--description-text)] hover-hover:hover:[--icons-color:inherit] focus-within:[--icons-color:inherit]";
457
- var RawTreeItem = ({ item, mode, open, current, draggable: _draggable, renderColumns: Columns, canDrop, onOpenChange, onSelect }) => {
458
- const { id, label, icon, className, headingClassName, disabled, path, parentOf } = item;
458
+ var TreeDataSchema = S.Struct({
459
+ id: S.String,
460
+ path: S.Array(S.String),
461
+ item: S.Any
462
+ });
463
+ var isTreeData = (data) => S.is(TreeDataSchema)(data);
464
+ var RawTreeItem = ({ item, path: _path, last, draggable: draggable2, renderColumns: Columns, canDrop, onOpenChange, onSelect }) => {
465
+ const { getItems, getProps, isOpen, isCurrent } = useTree();
466
+ const items = getItems(item);
467
+ const { id, label, parentOf, icon, disabled, className, headingClassName, testId } = getProps(item, _path);
468
+ const path = useMemo(() => [
469
+ ..._path,
470
+ id
471
+ ], [
472
+ _path,
473
+ id
474
+ ]);
475
+ const open = isOpen(path, item);
476
+ const current = isCurrent(path, item);
459
477
  const level = path.length - 2;
460
478
  const isBranch = !!parentOf;
479
+ const mode = last ? "last-in-group" : open ? "expanded" : "standard";
480
+ const data = useMemo(() => ({
481
+ id,
482
+ path,
483
+ item
484
+ }), [
485
+ id,
486
+ path,
487
+ item
488
+ ]);
461
489
  const rowRef = useRef2(null);
462
490
  const buttonRef = useRef2(null);
463
491
  const openRef = useRef2(false);
@@ -472,38 +500,46 @@ var RawTreeItem = ({ item, mode, open, current, draggable: _draggable, renderCol
472
500
  }
473
501
  }, []);
474
502
  useEffect3(() => {
475
- if (!_draggable) {
503
+ if (!draggable2) {
476
504
  return;
477
505
  }
478
506
  invariant2(buttonRef.current, void 0, {
479
507
  F: __dxlog_file2,
480
- L: 87,
508
+ L: 104,
481
509
  S: void 0,
482
510
  A: [
483
511
  "buttonRef.current",
484
512
  ""
485
513
  ]
486
514
  });
487
- return combine2(draggable2({
515
+ return combine2(pragmaticDraggable({
488
516
  element: buttonRef.current,
489
- getInitialData: () => item,
517
+ getInitialData: () => data,
490
518
  onDragStart: () => {
491
519
  setState("dragging");
492
520
  if (open) {
493
521
  openRef.current = true;
494
- onOpenChange?.(item, false);
522
+ onOpenChange?.({
523
+ item,
524
+ path,
525
+ open: false
526
+ });
495
527
  }
496
528
  },
497
529
  onDrop: () => {
498
530
  setState("idle");
499
531
  if (openRef.current) {
500
- onOpenChange?.(item, true);
532
+ onOpenChange?.({
533
+ item,
534
+ path,
535
+ open: true
536
+ });
501
537
  }
502
538
  }
503
539
  }), dropTargetForElements2({
504
540
  element: buttonRef.current,
505
541
  getData: ({ input, element }) => {
506
- return attachInstruction(item, {
542
+ return attachInstruction(data, {
507
543
  input,
508
544
  element,
509
545
  indentPerLevel: DEFAULT_INDENTATION,
@@ -516,15 +552,19 @@ var RawTreeItem = ({ item, mode, open, current, draggable: _draggable, renderCol
516
552
  },
517
553
  canDrop: ({ source }) => {
518
554
  const _canDrop = canDrop ?? (() => true);
519
- return source.element !== buttonRef.current && _canDrop(source.data, item);
555
+ return source.element !== buttonRef.current && _canDrop(source.data, data);
520
556
  },
521
557
  getIsSticky: () => true,
522
558
  onDrag: ({ self, source }) => {
523
559
  const instruction2 = extractInstruction(self.data);
524
- if (source.data.id !== item.id) {
560
+ if (source.data.id !== id) {
525
561
  if (instruction2?.type === "make-child" && isBranch && !open && !cancelExpandRef.current) {
526
562
  cancelExpandRef.current = setTimeout(() => {
527
- onOpenChange?.(item, true);
563
+ onOpenChange?.({
564
+ item,
565
+ path,
566
+ open: true
567
+ });
528
568
  }, 500);
529
569
  }
530
570
  if (instruction2?.type !== "make-child") {
@@ -549,24 +589,37 @@ var RawTreeItem = ({ item, mode, open, current, draggable: _draggable, renderCol
549
589
  }, [
550
590
  draggable2,
551
591
  item,
592
+ id,
552
593
  mode,
594
+ path,
553
595
  open,
554
596
  canDrop
555
597
  ]);
556
598
  useEffect3(() => () => cancelExpand(), [
557
599
  cancelExpand
558
600
  ]);
559
- const handleOpenChange = useCallback3(() => onOpenChange?.(item, !open), [
601
+ const handleOpenChange = useCallback3(() => onOpenChange?.({
602
+ item,
603
+ path,
604
+ open: !open
605
+ }), [
560
606
  onOpenChange,
561
607
  item,
608
+ path,
562
609
  open
563
610
  ]);
564
- const handleSelect = useCallback3(() => {
611
+ const handleSelect = useCallback3((option = false) => {
565
612
  rowRef.current?.focus();
566
- onSelect?.(item, !current);
613
+ onSelect?.({
614
+ item,
615
+ path,
616
+ current: !current,
617
+ option
618
+ });
567
619
  }, [
568
620
  onSelect,
569
621
  item,
622
+ path,
570
623
  current
571
624
  ]);
572
625
  const handleKeyDown = useCallback3((event) => {
@@ -578,7 +631,7 @@ var RawTreeItem = ({ item, mode, open, current, draggable: _draggable, renderCol
578
631
  isBranch && open && handleOpenChange();
579
632
  break;
580
633
  case " ":
581
- handleSelect();
634
+ handleSelect(event.altKey);
582
635
  break;
583
636
  }
584
637
  }, [
@@ -587,15 +640,15 @@ var RawTreeItem = ({ item, mode, open, current, draggable: _draggable, renderCol
587
640
  handleOpenChange,
588
641
  handleSelect
589
642
  ]);
590
- return /* @__PURE__ */ React7.createElement(Treegrid.Row, {
643
+ return /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Treegrid.Row, {
591
644
  ref: rowRef,
592
645
  key: id,
593
646
  id,
594
647
  "aria-labelledby": `${id}__label`,
595
648
  parentOf: parentOf?.join(Treegrid.PARENT_OF_SEPARATOR),
596
649
  classNames: mx6("grid grid-cols-subgrid col-[tree-row] mt-[2px] aria-[current]:bg-input", hoverableControls, hoverableFocusedKeyboardControls, hoverableFocusedWithinControls, hoverableDescriptionIcons, ghostHover, focusRing, className),
597
- "data-itemid": item.id,
598
- "data-testid": item.testId,
650
+ "data-itemid": id,
651
+ "data-testid": testId,
599
652
  // NOTE(thure): This is intentionally an empty string to for descendents to select by in the CSS
600
653
  // without alerting the user (except for in the correct link element). See also:
601
654
  // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current#description
@@ -626,61 +679,70 @@ var RawTreeItem = ({ item, mode, open, current, draggable: _draggable, renderCol
626
679
  onSelect: handleSelect
627
680
  })), Columns && /* @__PURE__ */ React7.createElement(Columns, {
628
681
  item,
682
+ path,
629
683
  menuOpen,
630
684
  setMenuOpen
631
685
  }), instruction && /* @__PURE__ */ React7.createElement(DropIndicator2, {
632
- instruction
686
+ instruction,
687
+ gap: 2
688
+ }))), open && items.map((item2, index) => /* @__PURE__ */ React7.createElement(TreeItem, {
689
+ key: item2.id,
690
+ item: item2,
691
+ path,
692
+ last: index === items.length - 1,
693
+ draggable: draggable2,
694
+ renderColumns: Columns,
695
+ canDrop,
696
+ onOpenChange,
697
+ onSelect
633
698
  })));
634
699
  };
635
700
  var TreeItem = /* @__PURE__ */ memo3(RawTreeItem);
636
701
 
637
702
  // packages/ui/react-ui-list/src/components/Tree/Tree.tsx
638
- var Tree = ({ items, open, current, draggable: draggable3 = false, gridTemplateColumns = "[tree-row-start] 1fr min-content [tree-row-end]", classNames, renderColumns, canDrop, onOpenChange, onSelect }) => {
703
+ var Tree = ({ id, getItems, getProps, isOpen, isCurrent, draggable: draggable2 = false, gridTemplateColumns = "[tree-row-start] 1fr min-content [tree-row-end]", classNames, renderColumns, canDrop, onOpenChange, onSelect }) => {
704
+ const context = useMemo2(() => ({
705
+ getItems,
706
+ getProps,
707
+ isOpen,
708
+ isCurrent
709
+ }), [
710
+ getItems,
711
+ getProps,
712
+ isOpen,
713
+ isCurrent
714
+ ]);
715
+ const items = getItems();
716
+ const path = useMemo2(() => [
717
+ id
718
+ ], [
719
+ id
720
+ ]);
639
721
  return /* @__PURE__ */ React8.createElement(Treegrid2.Root, {
640
722
  gridTemplateColumns,
641
723
  classNames
642
- }, items.map((item, i) => {
643
- const path = Path.create(...item.path);
644
- return /* @__PURE__ */ React8.createElement(TreeItem, {
645
- key: item.id,
646
- item,
647
- mode: getMode(items, i),
648
- open: open.includes(path),
649
- // TODO(wittjosiah): This should also be path-based.
650
- current: current.includes(item.id),
651
- draggable: draggable3,
652
- renderColumns,
653
- canDrop,
654
- onOpenChange,
655
- onSelect
656
- });
657
- }));
724
+ }, /* @__PURE__ */ React8.createElement(TreeProvider, {
725
+ value: context
726
+ }, items.map((item, index) => /* @__PURE__ */ React8.createElement(TreeItem, {
727
+ key: item.id,
728
+ item,
729
+ last: index === items.length - 1,
730
+ path,
731
+ draggable: draggable2,
732
+ renderColumns,
733
+ canDrop,
734
+ onOpenChange,
735
+ onSelect
736
+ }))));
658
737
  };
659
-
660
- // packages/ui/react-ui-list/src/components/Tree/types.ts
661
- import { S } from "@dxos/echo-schema";
662
- var LabelSchema = S.Union(S.String, S.Tuple(S.String, S.Struct({
663
- ns: S.String,
664
- count: S.optional(S.Number)
665
- })));
666
- var ItemSchema = S.mutable(S.Struct({
667
- id: S.String,
668
- label: S.mutable(LabelSchema),
669
- icon: S.optional(S.String),
670
- disabled: S.optional(S.Boolean),
671
- className: S.optional(S.String),
672
- headingClassName: S.optional(S.String),
673
- testId: S.optional(S.String),
674
- path: S.Array(S.String),
675
- parentOf: S.optional(S.Array(S.String))
676
- }));
677
- var isItem = S.is(ItemSchema);
678
738
  export {
679
- ItemSchema,
680
739
  List,
681
740
  RawTreeItem,
682
741
  Tree,
742
+ TreeDataSchema,
683
743
  TreeItem,
684
- isItem
744
+ TreeProvider,
745
+ isTreeData,
746
+ useTree
685
747
  };
686
748
  //# sourceMappingURL=index.mjs.map