@astryxdesign/lab 0.4.5-canary.bc34238 → 0.4.5-canary.ffd05c4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"TransferList.d.ts","sourceRoot":"","sources":["../../src/TransferList/TransferList.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TransferList.d.ts","sourceRoot":"","sources":["../../src/TransferList/TransferList.tsx"],"names":[],"mappings":"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"}
@@ -13,13 +13,14 @@
13
13
  * - /packages/lab/src/TransferList/TransferList.test.tsx
14
14
  * - /packages/lab/src/TransferList/index.ts
15
15
  */
16
- import { Fragment, useCallback, useId, useLayoutEffect, useMemo, useRef, useState } from 'react';
16
+ import { Fragment, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from 'react';
17
17
  import * as stylex from '@stylexjs/stylex';
18
- import { createPortal, flushSync } from 'react-dom';
18
+ import { flushSync } from 'react-dom';
19
19
  import { Button } from '@astryxdesign/core/Button';
20
20
  import { Icon } from '@astryxdesign/core/Icon';
21
21
  import { IconButton } from '@astryxdesign/core/IconButton';
22
22
  import { Item } from '@astryxdesign/core/Item';
23
+ import { useLayer } from '@astryxdesign/core/Layer';
23
24
  import { List } from '@astryxdesign/core/List';
24
25
  import { Text } from '@astryxdesign/core/Text';
25
26
  import { TextInput } from '@astryxdesign/core/TextInput';
@@ -171,17 +172,12 @@ const styles = {
171
172
  $$css: true
172
173
  }
173
174
  };
174
- const _temp = {
175
- k3aq6I: "xsqj5wx",
176
- "$$css": true
177
- };
178
175
  const dynamicStyles = {
179
- dragPreview: (x, y, width) => [_temp, {
176
+ dragPreview: width => [{
180
177
  kzqmXN: width != null ? "x5lhr3w" : width,
181
178
  $$css: true
182
179
  }, {
183
- "--x-width": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(width),
184
- "--x-transform": `translate3d(${x}px, ${y}px, 0)` != null ? `translate3d(${x}px, ${y}px, 0)` : undefined
180
+ "--x-width": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(width)
185
181
  }]
186
182
  };
187
183
  function moveItem(value, fromIndex, toIndex) {
@@ -256,6 +252,13 @@ export function TransferList({
256
252
  const availablePanelRef = useRef(null);
257
253
  const selectedRowRefs = useRef(new Map());
258
254
  const dragPreviewRef = useRef(null);
255
+ // The ghost that follows the pointer is a layer, not a portal into body.
256
+ // Its coordinates come from getBoundingClientRect, so they are viewport
257
+ // relative; the top layer is the only host that reads them that way from
258
+ // any scroll position and still paints above a Popover or open dialog.
259
+ const dragPreviewLayer = useLayer({
260
+ mode: 'fixed'
261
+ });
259
262
  const currentValueRef = useRef(value);
260
263
  const reorderSessionRef = useRef(null);
261
264
  const suppressClickRef = useRef(false);
@@ -666,7 +669,18 @@ export function TransferList({
666
669
  y: reorderSession.pointerClientY - reorderSession.pointerOffsetY,
667
670
  width: reorderSession.previewWidth
668
671
  } : null;
669
- const dragPreviewPortalTarget = typeof document === 'undefined' ? null : rootRef.current?.closest('[popover], dialog[open]') ?? document.body;
672
+ const isDraggingPreview = dragPreviewPosition != null;
673
+ const {
674
+ show: showDragPreview,
675
+ hide: hideDragPreview
676
+ } = dragPreviewLayer;
677
+ useEffect(() => {
678
+ if (!isDraggingPreview) {
679
+ return undefined;
680
+ }
681
+ showDragPreview();
682
+ return hideDragPreview;
683
+ }, [isDraggingPreview, showDragPreview, hideDragPreview]);
670
684
  const reorderControl = option => {
671
685
  if (!isReorderable) {
672
686
  return undefined;
@@ -897,12 +911,15 @@ export function TransferList({
897
911
  })
898
912
  })]
899
913
  })]
900
- }), dragPreviewPosition != null && dragPreviewPortalTarget != null ? /*#__PURE__*/createPortal(/*#__PURE__*/_jsx("div", {
914
+ }), dragPreviewPosition != null ? dragPreviewLayer.render(/*#__PURE__*/_jsx("div", {
901
915
  ref: dragPreviewRef,
902
916
  "aria-hidden": "true",
903
917
  "data-transfer-list-drag-preview": "",
904
- ...stylex.props(reorderStyles.preview, styles.dragPreviewContainer, dynamicStyles.dragPreview(dragPreviewPosition.x, dragPreviewPosition.y, dragPreviewPosition.width))
905
- }), dragPreviewPortalTarget) : null]
918
+ ...stylex.props(reorderStyles.preview, styles.dragPreviewContainer, dynamicStyles.dragPreview(dragPreviewPosition.width))
919
+ }), {
920
+ x: dragPreviewPosition.x,
921
+ y: dragPreviewPosition.y
922
+ }) : null]
906
923
  });
907
924
  }
908
925
  TransferList.displayName = 'TransferList';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/lab",
3
- "version": "0.4.5-canary.bc34238",
3
+ "version": "0.4.5-canary.ffd05c4",
4
4
  "description": "Experimental Astryx components — published to npm only under the @canary dist-tag for early testing; never released as a stable (latest) version.",
5
5
  "author": "Meta Open Source",
6
6
  "license": "MIT",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@stylexjs/stylex": ">=0.10.0",
48
- "@astryxdesign/core": "0.4.5-canary.bc34238",
48
+ "@astryxdesign/core": "0.4.5-canary.ffd05c4",
49
49
  "react": ">=19.0.0",
50
50
  "react-dom": ">=19.0.0"
51
51
  },
@@ -55,7 +55,7 @@
55
55
  "d3-shape": "^3.2.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@astryxdesign/cli": "0.4.5-canary.bc34238",
58
+ "@astryxdesign/cli": "0.4.5-canary.ffd05c4",
59
59
  "@types/d3-array": "^3.2.2",
60
60
  "@types/d3-scale": "^4.0.9",
61
61
  "@types/d3-shape": "^3.1.8",
@@ -482,6 +482,29 @@ export const docs = {
482
482
  },
483
483
  ],
484
484
  },
485
+ // Mirrors the themeProps() calls in TransferList.tsx. `astryx theme build`
486
+ // reads this inventory to learn which visual props a target can vary on, so
487
+ // a target missing here cannot be themed even though it carries the class.
488
+ theming: {
489
+ targets: [
490
+ {
491
+ className: 'astryx-transfer-list',
492
+ visualProps: [],
493
+ },
494
+ {
495
+ className: 'astryx-transfer-list-collection',
496
+ visualProps: [],
497
+ },
498
+ {
499
+ className: 'astryx-transfer-list-panel',
500
+ visualProps: ['side'],
501
+ },
502
+ {
503
+ className: 'astryx-transfer-list-item',
504
+ visualProps: ['side', 'state'],
505
+ },
506
+ ],
507
+ },
485
508
  examples: [
486
509
  {
487
510
  label: 'Default immediate selector',
@@ -496,7 +496,7 @@ describe('TransferList', () => {
496
496
  });
497
497
 
498
498
  describe('pointer reordering', () => {
499
- it('mounts the drag preview in the nearest top-layer container', () => {
499
+ it('promotes the drag preview to the top layer from inside a popover host', () => {
500
500
  const {container} = render(
501
501
  <div data-testid="popover-host" popover="auto">
502
502
  <ControlledTransferList initialValue={['name', 'owner', 'status']} />
@@ -521,7 +521,13 @@ describe('TransferList', () => {
521
521
  const preview = document.querySelector<HTMLElement>(
522
522
  '[data-transfer-list-drag-preview]',
523
523
  );
524
- expect(preview?.parentElement).toBe(screen.getByTestId('popover-host'));
524
+ // The ghost reaches the top layer through its host's popover attribute
525
+ // rather than by being relocated into an ancestor. That keeps it painted
526
+ // above this popover host while it still inherits the host's theme
527
+ // variables, which a portal to document.body would have dropped.
528
+ const layer = preview?.parentElement ?? null;
529
+ expect(layer).toHaveAttribute('popover');
530
+ expect(screen.getByTestId('popover-host')).toContainElement(layer);
525
531
  });
526
532
 
527
533
  it('keeps rows stationary, locks X, follows pointer Y, and commits once', () => {
@@ -560,9 +566,10 @@ describe('TransferList', () => {
560
566
  expect(getComputedStyle(preview!).opacity).toBe('0.5');
561
567
  expect(preview).toHaveAttribute('aria-hidden', 'true');
562
568
  expect(preview).toHaveTextContent('Owner');
563
- expect(preview!.style.getPropertyValue('--x-transform')).toBe(
564
- 'translate3d(0px, 40px, 0)',
565
- );
569
+ // Placement lives on the layer as fixed insets. The coordinates come
570
+ // from getBoundingClientRect, so reading them as viewport insets is what
571
+ // keeps the ghost under the pointer once the page has been scrolled.
572
+ expect(preview!.parentElement).toHaveStyle({top: '40px', left: '0px'});
566
573
  expect(
567
574
  container.querySelector('[data-transfer-list-drop-target]'),
568
575
  ).not.toBeInTheDocument();
@@ -573,9 +580,7 @@ describe('TransferList', () => {
573
580
  clientY: 5,
574
581
  });
575
582
 
576
- expect(preview!.style.getPropertyValue('--x-transform')).toBe(
577
- 'translate3d(0px, -5px, 0)',
578
- );
583
+ expect(preview!.parentElement).toHaveStyle({top: '-5px', left: '0px'});
579
584
  expect([
580
585
  ...container.querySelectorAll<HTMLElement>('[data-transfer-list-row]'),
581
586
  ]).toEqual(rows);
@@ -596,9 +601,7 @@ describe('TransferList', () => {
596
601
  clientY: 115,
597
602
  });
598
603
 
599
- expect(preview!.style.getPropertyValue('--x-transform')).toBe(
600
- 'translate3d(0px, 105px, 0)',
601
- );
604
+ expect(preview!.parentElement).toHaveStyle({top: '105px', left: '0px'});
602
605
  expect(rows[0]).not.toHaveAttribute('data-transfer-list-drop-target');
603
606
  expect(rows[2]).toHaveAttribute(
604
607
  'data-transfer-list-drop-target',
@@ -655,11 +658,12 @@ describe('TransferList', () => {
655
658
  expect(
656
659
  document.querySelector('[data-transfer-list-drag-preview]'),
657
660
  ).toBeInTheDocument();
661
+ // A horizontal-only gesture must not move the ghost off the row it came
662
+ // from: the drag is locked to the Y axis.
658
663
  expect(
659
- document
660
- .querySelector<HTMLElement>('[data-transfer-list-drag-preview]')
661
- ?.style.getPropertyValue('--x-transform'),
662
- ).toBe('translate3d(0px, 0px, 0)');
664
+ document.querySelector<HTMLElement>('[data-transfer-list-drag-preview]')
665
+ ?.parentElement,
666
+ ).toHaveStyle({top: '0px', left: '0px'});
663
667
  expect(
664
668
  container.querySelector('[data-transfer-list-drop-target]'),
665
669
  ).not.toBeInTheDocument();
@@ -17,6 +17,7 @@
17
17
  import {
18
18
  Fragment,
19
19
  useCallback,
20
+ useEffect,
20
21
  useId,
21
22
  useLayoutEffect,
22
23
  useMemo,
@@ -27,12 +28,13 @@ import {
27
28
  type ReactNode,
28
29
  } from 'react';
29
30
  import * as stylex from '@stylexjs/stylex';
30
- import {createPortal, flushSync} from 'react-dom';
31
+ import {flushSync} from 'react-dom';
31
32
  import type {BaseProps} from '@astryxdesign/core';
32
33
  import {Button} from '@astryxdesign/core/Button';
33
34
  import {Icon} from '@astryxdesign/core/Icon';
34
35
  import {IconButton} from '@astryxdesign/core/IconButton';
35
36
  import {Item} from '@astryxdesign/core/Item';
37
+ import {useLayer} from '@astryxdesign/core/Layer';
36
38
  import {List} from '@astryxdesign/core/List';
37
39
  import {Text} from '@astryxdesign/core/Text';
38
40
  import {TextInput} from '@astryxdesign/core/TextInput';
@@ -345,10 +347,10 @@ const styles = stylex.create({
345
347
  });
346
348
 
347
349
  const dynamicStyles = stylex.create({
348
- dragPreview: (x: number, y: number, width: number) => ({
349
- width,
350
- transform: `translate3d(${x}px, ${y}px, 0)`,
351
- }),
350
+ // Only the width is dynamic here. The layer owns the placement: it reads the
351
+ // same viewport coordinates as `position: fixed` insets, which is what
352
+ // getBoundingClientRect gives us.
353
+ dragPreview: (width: number) => ({width}),
352
354
  });
353
355
 
354
356
  function moveItem<T>(
@@ -435,6 +437,11 @@ export function TransferList<T extends string = string>({
435
437
  const availablePanelRef = useRef<HTMLDivElement | null>(null);
436
438
  const selectedRowRefs = useRef(new Map<T, HTMLElement>());
437
439
  const dragPreviewRef = useRef<HTMLDivElement | null>(null);
440
+ // The ghost that follows the pointer is a layer, not a portal into body.
441
+ // Its coordinates come from getBoundingClientRect, so they are viewport
442
+ // relative; the top layer is the only host that reads them that way from
443
+ // any scroll position and still paints above a Popover or open dialog.
444
+ const dragPreviewLayer = useLayer({mode: 'fixed'});
438
445
  const currentValueRef = useRef<readonly T[]>(value);
439
446
  const reorderSessionRef = useRef<ReorderSession<T> | null>(null);
440
447
  const suppressClickRef = useRef(false);
@@ -1010,11 +1017,15 @@ export function TransferList<T extends string = string>({
1010
1017
  width: reorderSession.previewWidth,
1011
1018
  }
1012
1019
  : null;
1013
- const dragPreviewPortalTarget =
1014
- typeof document === 'undefined'
1015
- ? null
1016
- : (rootRef.current?.closest<HTMLElement>('[popover], dialog[open]') ??
1017
- document.body);
1020
+ const isDraggingPreview = dragPreviewPosition != null;
1021
+ const {show: showDragPreview, hide: hideDragPreview} = dragPreviewLayer;
1022
+ useEffect(() => {
1023
+ if (!isDraggingPreview) {
1024
+ return undefined;
1025
+ }
1026
+ showDragPreview();
1027
+ return hideDragPreview;
1028
+ }, [isDraggingPreview, showDragPreview, hideDragPreview]);
1018
1029
 
1019
1030
  const reorderControl = (option: TransferListOption<T>) => {
1020
1031
  if (!isReorderable) {
@@ -1304,8 +1315,8 @@ export function TransferList<T extends string = string>({
1304
1315
  </div>
1305
1316
  </div>
1306
1317
  </div>
1307
- {dragPreviewPosition != null && dragPreviewPortalTarget != null
1308
- ? createPortal(
1318
+ {dragPreviewPosition != null
1319
+ ? dragPreviewLayer.render(
1309
1320
  <div
1310
1321
  ref={dragPreviewRef}
1311
1322
  aria-hidden="true"
@@ -1313,14 +1324,10 @@ export function TransferList<T extends string = string>({
1313
1324
  {...stylex.props(
1314
1325
  reorderStyles.preview,
1315
1326
  styles.dragPreviewContainer,
1316
- dynamicStyles.dragPreview(
1317
- dragPreviewPosition.x,
1318
- dragPreviewPosition.y,
1319
- dragPreviewPosition.width,
1320
- ),
1327
+ dynamicStyles.dragPreview(dragPreviewPosition.width),
1321
1328
  )}
1322
1329
  />,
1323
- dragPreviewPortalTarget,
1330
+ {x: dragPreviewPosition.x, y: dragPreviewPosition.y},
1324
1331
  )
1325
1332
  : null}
1326
1333
  </div>