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