@astryxdesign/lab 0.7.7-canary.c0929c8 → 0.7.7-canary.c19ca12

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 (31) hide show
  1. package/dist/InfoTip/index.d.ts +21 -3
  2. package/dist/InfoTip/index.d.ts.map +1 -1
  3. package/dist/InfoTip/index.js +22 -2
  4. package/dist/MobilePowerSearch/MobilePowerSearch.d.ts.map +1 -1
  5. package/dist/MobilePowerSearch/MobilePowerSearch.js +5 -4
  6. package/dist/MobileTokenizer/MobileTokenizer.d.ts +1 -1
  7. package/dist/MobileTokenizer/MobileTokenizer.d.ts.map +1 -1
  8. package/dist/MobileTokenizer/MobileTokenizer.js +10 -37
  9. package/dist/lab.css +0 -3
  10. package/package.json +5 -5
  11. package/src/InfoTip/InfoTip.test.tsx +24 -229
  12. package/src/InfoTip/index.ts +28 -3
  13. package/src/MobilePowerSearch/MobilePowerSearch.test.tsx +11 -0
  14. package/src/MobilePowerSearch/MobilePowerSearch.tsx +18 -7
  15. package/src/MobileTokenizer/MobileTokenizer.test.tsx +6 -5
  16. package/src/MobileTokenizer/MobileTokenizer.tsx +25 -51
  17. package/dist/InfoTip/InfoTip.d.ts +0 -58
  18. package/dist/InfoTip/InfoTip.d.ts.map +0 -1
  19. package/dist/InfoTip/InfoTip.js +0 -94
  20. package/dist/internal/MobileSheetConfirmation.d.ts +0 -12
  21. package/dist/internal/MobileSheetConfirmation.d.ts.map +0 -1
  22. package/dist/internal/MobileSheetConfirmation.js +0 -88
  23. package/dist/internal/useDismissSearchOnTouchDrag.d.ts +0 -21
  24. package/dist/internal/useDismissSearchOnTouchDrag.d.ts.map +0 -1
  25. package/dist/internal/useDismissSearchOnTouchDrag.js +0 -43
  26. package/src/InfoTip/InfoTip.doc.mjs +0 -100
  27. package/src/InfoTip/InfoTip.tsx +0 -128
  28. package/src/internal/MobileSheetConfirmation.test.tsx +0 -68
  29. package/src/internal/MobileSheetConfirmation.tsx +0 -97
  30. package/src/internal/useDismissSearchOnTouchDrag.test.tsx +0 -94
  31. package/src/internal/useDismissSearchOnTouchDrag.ts +0 -57
@@ -142,8 +142,11 @@ import {
142
142
  typographyVars,
143
143
  } from '@astryxdesign/core/theme/tokens.stylex';
144
144
  import {isImeKeyEvent, isRenderable, rtlStyles} from '@astryxdesign/core/utils';
145
- import {MobileSheetConfirmation} from '../internal/MobileSheetConfirmation';
146
- import {useDismissSearchOnTouchDrag} from '../internal/useDismissSearchOnTouchDrag';
145
+
146
+ import {
147
+ SheetConfirmation,
148
+ useDismissSearchOnTouchDrag,
149
+ } from '@astryxdesign/core/internal/touch-sheet';
147
150
  import {MobilePowerSearchValueEditor} from './MobilePowerSearchValueEditor';
148
151
  import {
149
152
  createContentSearchSource,
@@ -889,10 +892,18 @@ export function MobilePowerSearch({
889
892
 
890
893
  // ------------------------------------------------------------------ focus --
891
894
 
892
- const fieldPickerDragProps = useDismissSearchOnTouchDrag(() => [
893
- contentSearchRef.current?.querySelector('input'),
894
- fieldSearchRef.current?.querySelector('input'),
895
- ]);
895
+ // A drag moves focus to the sheet's panel, never to the page body behind
896
+ // the open modal.
897
+ const fieldPickerDragProps = useDismissSearchOnTouchDrag(
898
+ () => [
899
+ contentSearchRef.current?.querySelector('input'),
900
+ fieldSearchRef.current?.querySelector('input'),
901
+ ],
902
+ () =>
903
+ (
904
+ fieldSearchRef.current ?? contentSearchRef.current
905
+ )?.closest<HTMLElement>('[tabindex="-1"]'),
906
+ );
896
907
 
897
908
  const focusPendingSheetTarget = useCallback(() => {
898
909
  const target = pendingSheetFocusRef.current;
@@ -2356,7 +2367,7 @@ export function MobilePowerSearch({
2356
2367
  </div>
2357
2368
  </BottomSheet>
2358
2369
 
2359
- <MobileSheetConfirmation
2370
+ <SheetConfirmation
2360
2371
  isOpen={isClearConfirmationOpen}
2361
2372
  onOpenChange={setIsClearConfirmationOpen}
2362
2373
  title="Clear all filters?"
@@ -185,6 +185,9 @@ describe('MobileTokenizer (Lab, single-sheet flow)', () => {
185
185
  let discard = await screen.findByRole('alertdialog', {
186
186
  name: 'Discard changes?',
187
187
  });
188
+ // A scrim press starts and ends on the dialog itself.
189
+ fireEvent.pointerDown(discard);
190
+ fireEvent.pointerUp(discard);
188
191
  fireEvent.click(discard);
189
192
  await waitFor(() =>
190
193
  expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument(),
@@ -1245,6 +1248,7 @@ describe('MobileTokenizer Core-sheet parity', () => {
1245
1248
  );
1246
1249
  }
1247
1250
  render(<Flow />);
1251
+ const trigger = screen.getByRole('button', {name: /Tags/});
1248
1252
  const sheet = await openSheet();
1249
1253
  // Pending from open until the bootstrap settles.
1250
1254
  expect(within(sheet).getByRole('img', {name: 'Loading'})).toBeTruthy();
@@ -1261,11 +1265,8 @@ describe('MobileTokenizer Core-sheet parity', () => {
1261
1265
  // The field becomes disabled while open: the sheet closes and the query
1262
1266
  // is kept; re-enabled and reopened, it starts from "en", settled.
1263
1267
  fireEvent.click(screen.getByRole('button', {name: 'Toggle'}));
1264
- expect(screen.getByRole('button', {name: /Tags/})).toHaveAttribute(
1265
- 'aria-expanded',
1266
- 'false',
1267
- );
1268
- act(() => screen.getByRole('button', {name: /Tags/}).focus());
1268
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
1269
+ act(() => trigger.focus());
1269
1270
  await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
1270
1271
  fireEvent.click(screen.getByRole('button', {name: 'Toggle'}));
1271
1272
  const reopened = await openSheet();
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * @file MobileTokenizer.tsx
7
7
  * @input Public @astryxdesign/core components and hooks, plus private Lab
8
- * mobile-sheet confirmation and search-drag dismissal owners
8
+ * touch-sheet confirmation and search-drag dismissal from Core's internal entry
9
9
  * @output Exports MobileTokenizer — Lab prototype of the touch Tokenizer
10
10
  * flow with the inline-compatible closed trigger, a searchable draft-selection
11
11
  * sheet, persistent Clear all / Save footer, dirty-dismiss confirmation,
@@ -72,8 +72,13 @@ import {
72
72
  sizeVars,
73
73
  spacingVars,
74
74
  } from '@astryxdesign/core/theme/tokens.stylex';
75
- import {MobileSheetConfirmation} from '../internal/MobileSheetConfirmation';
76
- import {useDismissSearchOnTouchDrag} from '../internal/useDismissSearchOnTouchDrag';
75
+ import {
76
+ SheetConfirmation,
77
+ hasSameSelection,
78
+ nameSheetDraftChange,
79
+ useDismissSearchOnTouchDrag,
80
+ type SheetDraftChange,
81
+ } from '@astryxdesign/core/internal/touch-sheet';
77
82
 
78
83
  export type MobileTokenizerChange<T extends SearchableItem> =
79
84
  | {item: T; type: 'add'}
@@ -130,18 +135,6 @@ const LIST_LOAD_MORE_THRESHOLD_PX = 200;
130
135
  // border = 3px. The same number is the row gap, so wrapped rows read evenly.
131
136
  const TOKEN_ROW_INSET = `calc(${spacingVars['--spacing-1']} - 1px)`;
132
137
 
133
- type SheetDraftChange<T> = {
134
- item: T;
135
- type: 'add' | 'create' | 'remove';
136
- };
137
-
138
- function hasSameSelection<T extends SearchableItem>(a: T[], b: T[]): boolean {
139
- return (
140
- a.length === b.length &&
141
- a.every(item => b.some(candidate => candidate.id === item.id))
142
- );
143
- }
144
-
145
138
  const styles = stylex.create({
146
139
  // The closed trigger is the inline Tokenizer field's own control box, as a
147
140
  // button — the same `inputWrapperStyles.base` box Core's sheet trigger
@@ -1018,39 +1011,15 @@ function MobileTokenizerSheetContent<T extends SearchableItem>({
1018
1011
  if (isDraftOverMax) {
1019
1012
  return;
1020
1013
  }
1021
- if (isDraftDirty) {
1022
- const added = draftValue.find(
1023
- item => !valueAtOpen.some(openItem => openItem.id === item.id),
1024
- );
1025
- const removed = valueAtOpen.find(
1026
- item => !draftValue.some(draftItem => draftItem.id === item.id),
1027
- );
1028
- const lastDraftChange = lastDraftChangeRef.current;
1029
- const lastChangeIsNet =
1030
- lastDraftChange != null &&
1031
- (lastDraftChange.type === 'remove'
1032
- ? valueAtOpen.some(item => item.id === lastDraftChange.item.id) &&
1033
- !draftValue.some(item => item.id === lastDraftChange.item.id)
1034
- : draftValue.some(item => item.id === lastDraftChange.item.id) &&
1035
- !valueAtOpen.some(item => item.id === lastDraftChange.item.id));
1036
- let change: SheetDraftChange<T> | null = null;
1037
- if (lastChangeIsNet) {
1038
- change = lastDraftChange;
1039
- } else if (lastDraftChange?.type === 'remove' && removed != null) {
1040
- change = {item: removed, type: 'remove'};
1041
- } else if (added != null) {
1042
- const type: SheetDraftChange<T>['type'] =
1043
- lastDraftChange?.item.id === added.id &&
1044
- lastDraftChange.type === 'create'
1045
- ? 'create'
1046
- : 'add';
1047
- change = {item: added, type};
1048
- } else if (removed != null) {
1049
- change = {item: removed, type: 'remove'};
1050
- }
1051
- if (change != null) {
1052
- onCommit([...draftValue], change);
1053
- }
1014
+ const change = isDraftDirty
1015
+ ? nameSheetDraftChange(
1016
+ valueAtOpen,
1017
+ draftValue,
1018
+ lastDraftChangeRef.current,
1019
+ )
1020
+ : null;
1021
+ if (change != null) {
1022
+ onCommit([...draftValue], change);
1054
1023
  }
1055
1024
  onCloseSheet();
1056
1025
  };
@@ -1058,7 +1027,12 @@ function MobileTokenizerSheetContent<T extends SearchableItem>({
1058
1027
  const searchLabel = t('@astryx.tokenizer.search', {label});
1059
1028
  const noResultsTitle =
1060
1029
  emptySearchResultsText ?? t('@astryx.typeahead.emptySearchResults');
1061
- const listDragProps = useDismissSearchOnTouchDrag(() => [searchRef.current]);
1030
+ // A drag moves focus to the sheet's panel, never to the page body behind
1031
+ // the open modal.
1032
+ const listDragProps = useDismissSearchOnTouchDrag(
1033
+ () => [searchRef.current],
1034
+ () => searchRef.current?.closest<HTMLElement>('[tabindex="-1"]'),
1035
+ );
1062
1036
 
1063
1037
  return (
1064
1038
  <div {...stylex.props(styles.sheetBody)}>
@@ -1245,7 +1219,7 @@ function MobileTokenizerSheetContent<T extends SearchableItem>({
1245
1219
  />
1246
1220
  </div>
1247
1221
  {isClearConfirmationOpen && (
1248
- <MobileSheetConfirmation
1222
+ <SheetConfirmation
1249
1223
  isOpen
1250
1224
  onOpenChange={next => {
1251
1225
  if (!next) {
@@ -1270,7 +1244,7 @@ function MobileTokenizerSheetContent<T extends SearchableItem>({
1270
1244
  />
1271
1245
  )}
1272
1246
  {isDiscardConfirmationOpen && (
1273
- <MobileSheetConfirmation
1247
+ <SheetConfirmation
1274
1248
  isOpen
1275
1249
  onOpenChange={next => {
1276
1250
  if (!next) {
@@ -1,58 +0,0 @@
1
- /**
2
- * @file InfoTip.tsx
3
- * @input Uses React, stylex, Tooltip + Icon from @astryxdesign/core, color/spacing/radius/duration/ease tokens
4
- * @output Exports InfoTip component, InfoTipProps, InfoTipSize types
5
- * @position Lab experiment (RFC meta/astryx#3349); core implementation consumed by index.ts
6
- *
7
- * SYNC: When modified, update these files to stay in sync:
8
- * - /packages/lab/src/InfoTip/InfoTip.doc.mjs (props table, features)
9
- * - /packages/lab/src/InfoTip/InfoTip.test.tsx (tests for new/changed behavior)
10
- * - /packages/lab/src/InfoTip/index.ts (exports if types change)
11
- */
12
- import { type ReactNode } from 'react';
13
- import { type IconSize } from '@astryxdesign/core/Icon';
14
- /**
15
- * Size of the info icon. Maps 1:1 to Icon sizes
16
- * (xsm: 12px, sm: 16px, md: 20px, lg: 24px).
17
- */
18
- export type InfoTipSize = IconSize;
19
- export interface InfoTipProps {
20
- /**
21
- * Content to display in the tooltip.
22
- * Typically short, non-interactive text. Mirrors Tooltip's `content` prop.
23
- */
24
- content: ReactNode;
25
- /**
26
- * Accessible name for the trigger button.
27
- * @default 'More information'
28
- */
29
- label?: string;
30
- /**
31
- * Size of the info icon.
32
- * @default 'sm'
33
- */
34
- size?: InfoTipSize;
35
- }
36
- /**
37
- * An inline info-icon help affordance: a small "i" button that reveals a
38
- * tooltip on hover, keyboard focus, and tap. Use it next to labels, values,
39
- * and metrics for permission notes, metric definitions, and field help.
40
- *
41
- * The value over hand-composing Icon inside Tooltip is the pre-wired
42
- * accessible trigger: a real button with an aria-label, Tab-reachable,
43
- * tooltip on hover AND focus, and Escape dismissal.
44
- *
45
- * Composed entirely from core primitives (Tooltip + Icon); the info icon
46
- * resolves from the global icon registry, so themes can override it.
47
- *
48
- * @example
49
- * ```
50
- * <InfoTip content="Editors can change this field; viewers cannot." />
51
- * <InfoTip content="30-day rolling average." label="About this metric" />
52
- * ```
53
- */
54
- export declare function InfoTip({ content, label, size, }: InfoTipProps): ReactNode;
55
- export declare namespace InfoTip {
56
- var displayName: string;
57
- }
58
- //# sourceMappingURL=InfoTip.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"InfoTip.d.ts","sourceRoot":"","sources":["../../src/InfoTip/InfoTip.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAAC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAGrC,OAAO,EAAO,KAAK,QAAQ,EAAC,MAAM,yBAAyB,CAAC;AAW5D;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;AAEnC,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,OAAO,EAAE,SAAS,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AA8BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,KAA0B,EAC1B,IAAW,GACZ,EAAE,YAAY,GAAG,SAAS,CAqB1B;yBAzBe,OAAO"}
@@ -1,94 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- 'use client';
4
-
5
- /**
6
- * @file InfoTip.tsx
7
- * @input Uses React, stylex, Tooltip + Icon from @astryxdesign/core, color/spacing/radius/duration/ease tokens
8
- * @output Exports InfoTip component, InfoTipProps, InfoTipSize types
9
- * @position Lab experiment (RFC meta/astryx#3349); core implementation consumed by index.ts
10
- *
11
- * SYNC: When modified, update these files to stay in sync:
12
- * - /packages/lab/src/InfoTip/InfoTip.doc.mjs (props table, features)
13
- * - /packages/lab/src/InfoTip/InfoTip.test.tsx (tests for new/changed behavior)
14
- * - /packages/lab/src/InfoTip/index.ts (exports if types change)
15
- */
16
- import * as stylex from '@stylexjs/stylex';
17
- import { Icon } from '@astryxdesign/core/Icon';
18
- import { Tooltip } from '@astryxdesign/core/Tooltip';
19
- import '@astryxdesign/core/theme/tokens.stylex';
20
- import { colorVars, durationVars, easeVars, radiusVars, spacingVars } from '@astryxdesign/core/theme/tokens.stylex';
21
- import { focusOutlineStyles } from '@astryxdesign/core/utils';
22
-
23
- /**
24
- * Size of the info icon. Maps 1:1 to Icon sizes
25
- * (xsm: 12px, sm: 16px, md: 20px, lg: 24px).
26
- */
27
- import { jsx as _jsx } from "react/jsx-runtime";
28
- const styles = {
29
- trigger: {
30
- k1xSpc: "x3nfvp2",
31
- kGNEyG: "x6s0dn4",
32
- kjj79g: "xl56j7k",
33
- kXLuUW: "xxymvpz",
34
- kmVPX3: "x12nba7r",
35
- kogj98: "x1ghz6dp",
36
- ksu8eU: "xng3xce",
37
- kWkggS: "xjbqb8w",
38
- kaIpWk: "xjspbzw",
39
- kkrTdU: "x1ypdohk x16khyan",
40
- kMwMTN: "xv9yike x10ue8fs",
41
- k1ekBW: "xt3l3uh",
42
- kIyJzY: "xuedmi6",
43
- kAMwcw: "xlr8y92",
44
- $$css: true
45
- }
46
- };
47
-
48
- /**
49
- * An inline info-icon help affordance: a small "i" button that reveals a
50
- * tooltip on hover, keyboard focus, and tap. Use it next to labels, values,
51
- * and metrics for permission notes, metric definitions, and field help.
52
- *
53
- * The value over hand-composing Icon inside Tooltip is the pre-wired
54
- * accessible trigger: a real button with an aria-label, Tab-reachable,
55
- * tooltip on hover AND focus, and Escape dismissal.
56
- *
57
- * Composed entirely from core primitives (Tooltip + Icon); the info icon
58
- * resolves from the global icon registry, so themes can override it.
59
- *
60
- * @example
61
- * ```
62
- * <InfoTip content="Editors can change this field; viewers cannot." />
63
- * <InfoTip content="30-day rolling average." label="About this metric" />
64
- * ```
65
- */
66
- export function InfoTip({
67
- content,
68
- label = 'More information',
69
- size = 'sm'
70
- }) {
71
- // No local Escape handler: the trigger used to stopPropagation the press,
72
- // which hid the tip but left the browser's close watcher to fire `cancel` on
73
- // an enclosing Dialog, so one press took both (#5168). Tooltip's own entry in
74
- // the shared dismissal stack takes it instead.
75
- return /*#__PURE__*/_jsx(Tooltip, {
76
- content: content
77
- // The trigger is a real button, so Tooltip's `auto` touch rule would
78
- // give the tap to the control and suppress the tooltip. Here the tooltip
79
- // IS the control's only purpose, so the tap has to open it — otherwise
80
- // an InfoTip's content is unreachable on a phone.
81
- ,
82
- touchTrigger: "tap",
83
- children: /*#__PURE__*/_jsx("button", {
84
- type: "button",
85
- "aria-label": label,
86
- ...stylex.props(focusOutlineStyles.focusVisible, styles.trigger),
87
- children: /*#__PURE__*/_jsx(Icon, {
88
- icon: "info",
89
- size: size
90
- })
91
- })
92
- });
93
- }
94
- InfoTip.displayName = 'InfoTip';
@@ -1,12 +0,0 @@
1
- interface MobileSheetConfirmationProps {
2
- isOpen: boolean;
3
- onOpenChange: (isOpen: boolean) => void;
4
- title: string;
5
- description: string;
6
- cancelLabel: string;
7
- actionLabel: string;
8
- onAction: () => void;
9
- }
10
- export declare function MobileSheetConfirmation({ isOpen, onOpenChange, title, description, cancelLabel, actionLabel, onAction, }: MobileSheetConfirmationProps): import("react").JSX.Element;
11
- export {};
12
- //# sourceMappingURL=MobileSheetConfirmation.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MobileSheetConfirmation.d.ts","sourceRoot":"","sources":["../../src/internal/MobileSheetConfirmation.tsx"],"names":[],"mappings":"AA0BA,UAAU,4BAA4B;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAMD,wBAAgB,uBAAuB,CAAC,EACtC,MAAM,EACN,YAAY,EACZ,KAAK,EACL,WAAW,EACX,WAAW,EACX,WAAW,EACX,QAAQ,GACT,EAAE,4BAA4B,+BAgD9B"}
@@ -1,88 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- 'use client';
4
-
5
- /**
6
- * @file MobileSheetConfirmation.tsx
7
- * @input Public Core Dialog, layout, text and action primitives; caller-owned copy
8
- * @output Private confirmation shared by MobileTokenizer and MobilePowerSearch
9
- * @position Lab-only mobile sheet confirmation owner; not a public export
10
- *
11
- * Core AlertDialog fixes its purpose to form and its safe action to ghost.
12
- * These Lab flows instead allow scrim dismissal and use separated, full-width
13
- * large actions with the secondary safe action focused first. Keep that
14
- * difference here, while preserving the existing alert-dialog theme target.
15
- */
16
- import { useId } from 'react';
17
- import * as stylex from '@stylexjs/stylex';
18
- import { Button } from '@astryxdesign/core/Button';
19
- import { Dialog } from '@astryxdesign/core/Dialog';
20
- import { Heading } from '@astryxdesign/core/Heading';
21
- import { Layout, LayoutContent, LayoutFooter } from '@astryxdesign/core/Layout';
22
- import { Stack } from '@astryxdesign/core/Stack';
23
- import { Text } from '@astryxdesign/core/Text';
24
- import { themeProps } from '@astryxdesign/core/utils';
25
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
26
- const styles = {
27
- action: {
28
- kzqmXN: "xh8yej3",
29
- $$css: true
30
- }
31
- };
32
- export function MobileSheetConfirmation({
33
- isOpen,
34
- onOpenChange,
35
- title,
36
- description,
37
- cancelLabel,
38
- actionLabel,
39
- onAction
40
- }) {
41
- const titleId = useId();
42
- const descriptionId = useId();
43
- return /*#__PURE__*/_jsx(Dialog, {
44
- ...themeProps('alert-dialog'),
45
- isOpen: isOpen,
46
- onOpenChange: onOpenChange,
47
- purpose: "info",
48
- role: "alertdialog",
49
- "aria-labelledby": titleId,
50
- "aria-describedby": descriptionId,
51
- width: 400,
52
- children: /*#__PURE__*/_jsx(Layout, {
53
- content: /*#__PURE__*/_jsxs(LayoutContent, {
54
- children: [/*#__PURE__*/_jsx(Heading, {
55
- level: 2,
56
- id: titleId,
57
- children: title
58
- }), /*#__PURE__*/_jsx(Text, {
59
- type: "body",
60
- color: "secondary",
61
- id: descriptionId,
62
- children: description
63
- })]
64
- }),
65
- footer: /*#__PURE__*/_jsx(LayoutFooter, {
66
- children: /*#__PURE__*/_jsxs(Stack, {
67
- direction: "vertical",
68
- gap: 4,
69
- hAlign: "stretch",
70
- children: [/*#__PURE__*/_jsx(Button, {
71
- label: actionLabel,
72
- variant: "destructive",
73
- size: "lg",
74
- onClick: onAction,
75
- xstyle: styles.action
76
- }), /*#__PURE__*/_jsx(Button, {
77
- label: cancelLabel,
78
- variant: "secondary",
79
- size: "lg",
80
- onClick: () => onOpenChange(false),
81
- "data-autofocus": true,
82
- xstyle: styles.action
83
- })]
84
- })
85
- })
86
- })
87
- });
88
- }
@@ -1,21 +0,0 @@
1
- /**
2
- * @file useDismissSearchOnTouchDrag.ts
3
- * @input Caller-owned search inputs and touch events from a separate results list
4
- * @output Private touch handlers shared by the Lab mobile search sheets
5
- * @position One owner for results-drag keyboard dismissal; not a public export
6
- *
7
- * A direct vertical touch drag dismisses only a focused caller-owned search.
8
- * Scroll events are deliberately not observed: filtering can clamp scrollTop
9
- * and fire scroll while someone is typing. Small moves, taps and multi-touch
10
- * gestures leave focus alone. The hook acquires no external resource.
11
- */
12
- import { type TouchEvent } from 'react';
13
- type SearchInput = HTMLInputElement | null | undefined;
14
- export declare function useDismissSearchOnTouchDrag(getSearchInputs: () => readonly SearchInput[]): {
15
- onTouchStart: (event: TouchEvent<HTMLElement>) => void;
16
- onTouchMove: (event: TouchEvent<HTMLElement>) => void;
17
- onTouchEnd: () => void;
18
- onTouchCancel: () => void;
19
- };
20
- export {};
21
- //# sourceMappingURL=useDismissSearchOnTouchDrag.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useDismissSearchOnTouchDrag.d.ts","sourceRoot":"","sources":["../../src/internal/useDismissSearchOnTouchDrag.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAAS,KAAK,UAAU,EAAC,MAAM,OAAO,CAAC;AAI9C,KAAK,WAAW,GAAG,gBAAgB,GAAG,IAAI,GAAG,SAAS,CAAC;AAEvD,wBAAgB,2BAA2B,CACzC,eAAe,EAAE,MAAM,SAAS,WAAW,EAAE;0BAQrB,UAAU,CAAC,WAAW,CAAC;yBAIxB,UAAU,CAAC,WAAW,CAAC;;;EAqB/C"}
@@ -1,43 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- 'use client';
4
-
5
- /**
6
- * @file useDismissSearchOnTouchDrag.ts
7
- * @input Caller-owned search inputs and touch events from a separate results list
8
- * @output Private touch handlers shared by the Lab mobile search sheets
9
- * @position One owner for results-drag keyboard dismissal; not a public export
10
- *
11
- * A direct vertical touch drag dismisses only a focused caller-owned search.
12
- * Scroll events are deliberately not observed: filtering can clamp scrollTop
13
- * and fire scroll while someone is typing. Small moves, taps and multi-touch
14
- * gestures leave focus alone. The hook acquires no external resource.
15
- */
16
- import { useRef } from 'react';
17
- const DRAG_THRESHOLD_PX = 8;
18
- export function useDismissSearchOnTouchDrag(getSearchInputs) {
19
- const startYRef = useRef(null);
20
- const reset = () => {
21
- startYRef.current = null;
22
- };
23
- return {
24
- onTouchStart: event => {
25
- startYRef.current = event.touches.length === 1 ? event.touches[0].clientY : null;
26
- },
27
- onTouchMove: event => {
28
- if (event.touches.length !== 1) {
29
- reset();
30
- return;
31
- }
32
- const startY = startYRef.current;
33
- if (startY == null || Math.abs(event.touches[0].clientY - startY) < DRAG_THRESHOLD_PX) {
34
- return;
35
- }
36
- reset();
37
- const focusedSearch = getSearchInputs().find(input => input != null && input === document.activeElement);
38
- focusedSearch?.blur();
39
- },
40
- onTouchEnd: reset,
41
- onTouchCancel: reset
42
- };
43
- }
@@ -1,100 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- /** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
4
-
5
- export const docs = {
6
- name: 'InfoTip',
7
- displayName: 'Info Tip',
8
- category: 'Feedback & Status',
9
- keywords: ["info","tooltip","help","hint","affordance","icon","label","field","metric","definition"],
10
- props: [
11
- {
12
- name: 'content',
13
- type: 'ReactNode',
14
- description: 'Content to display in the tooltip. Typically short, non-interactive text. Mirrors Tooltip\'s content prop.',
15
- required: true,
16
- },
17
- {
18
- name: 'label',
19
- type: 'string',
20
- description: 'Accessible name for the trigger button.',
21
- default: "'More information'",
22
- },
23
- {
24
- name: 'size',
25
- type: "'xsm' | 'sm' | 'md' | 'lg'",
26
- description: 'Size of the info icon (12px, 16px, 20px, 24px). Maps 1:1 to Icon sizes.',
27
- default: "'sm'",
28
- },
29
- ],
30
- usage: {
31
- description:
32
- 'An inline info-icon help affordance: a small "i" button that reveals a tooltip on hover, keyboard focus, and tap. Use it next to labels, values, and metrics for permission notes, metric definitions, and field help. Its value over hand-composing Icon inside Tooltip is the pre-wired accessible trigger: a real button with an aria-label, Tab-reachable, tooltip on hover AND focus AND tap, and Escape dismissal.',
33
- bestPractices: [
34
- { guidance: true, description: 'Keep tooltip content concise, plain, and non-interactive; use Popover or HoverCard for links and buttons.' },
35
- { guidance: true, description: 'Override label when "More information" is too generic, e.g. "About this metric".' },
36
- { guidance: true, description: 'Rely on InfoTip rather than a hand-rolled info button on touch: it sets Tooltip touchTrigger to tap, so a tap opens the tooltip even though the trigger is a button, and a tap outside dismisses it.' },
37
- { guidance: false, description: 'Hand-compose Icon inside Tooltip for info affordances; the bare Icon is aria-hidden and unfocusable, so keyboard and screen-reader users never see it.' },
38
- { guidance: false, description: 'Use InfoTip for essential information users must see to complete a task; put that text inline instead.' },
39
- ],
40
- },
41
- };
42
-
43
- /** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
44
- export const docsZh = {
45
- name: 'InfoTip',
46
- displayName: 'Info Tip',
47
- props: [
48
- {
49
- name: 'content',
50
- type: 'ReactNode',
51
- description: '工具提示中显示的内容。通常为简短的非交互文本。与 Tooltip 的 content 属性一致。',
52
- required: true,
53
- },
54
- {
55
- name: 'label',
56
- type: 'string',
57
- description: '触发按钮的无障碍名称。',
58
- default: "'More information'",
59
- },
60
- {
61
- name: 'size',
62
- type: "'xsm' | 'sm' | 'md' | 'lg'",
63
- description: '信息图标的尺寸(12px、16px、20px、24px)。与 Icon 尺寸一一对应。',
64
- default: "'sm'",
65
- },
66
- ],
67
- usage: {
68
- description:
69
- 'An inline info-icon help affordance: a small "i" button that reveals a tooltip on hover, keyboard focus, and tap. Use it next to labels, values, and metrics for permission notes, metric definitions, and field help.',
70
- bestPractices: [
71
- { guidance: true, description: 'Keep tooltip content concise, plain, and non-interactive; use Popover or HoverCard for links and buttons.' },
72
- { guidance: true, description: 'Override label when "More information" is too generic, e.g. "About this metric".' },
73
- { guidance: true, description: 'Rely on InfoTip rather than a hand-rolled info button on touch: it sets Tooltip touchTrigger to tap, so a tap opens the tooltip even though the trigger is a button, and a tap outside dismisses it.' },
74
- { guidance: false, description: 'Hand-compose Icon inside Tooltip for info affordances; the bare Icon is aria-hidden and unfocusable, so keyboard and screen-reader users never see it.' },
75
- { guidance: false, description: 'Use InfoTip for essential information users must see to complete a task; put that text inline instead.' },
76
- ],
77
- },
78
- };
79
-
80
- /** @type {import('@astryxdesign/cli/authoring').ComponentTranslationDoc} */
81
- export const docsDense = {
82
- description:
83
- 'Inline info-icon help affordance: accessible "i" button trigger pre-wired into a Tooltip.',
84
- usage: {
85
- description:
86
- 'A small "i" button that reveals a tooltip on hover, keyboard focus, and tap. Use next to labels, values, and metrics for permission notes, metric definitions, and field help. Pre-wired accessible trigger: real button, aria-label, Tab-reachable, tooltip on hover AND focus AND tap, Escape dismissal.',
87
- bestPractices: [
88
- { guidance: true, description: 'Keep tooltip content concise, plain, and non-interactive; use Popover or HoverCard for links and buttons.' },
89
- { guidance: true, description: 'Override label when "More information" is too generic, e.g. "About this metric".' },
90
- { guidance: true, description: 'Rely on InfoTip rather than a hand-rolled info button on touch: it sets Tooltip touchTrigger to tap, so a tap opens the tooltip even though the trigger is a button, and a tap outside dismisses it.' },
91
- { guidance: false, description: 'Hand-compose Icon inside Tooltip for info affordances; the bare Icon is aria-hidden and unfocusable.' },
92
- { guidance: false, description: 'Use InfoTip for essential information users must see to complete a task; put that text inline instead.' },
93
- ],
94
- },
95
- propDescriptions: {
96
- content: 'Tooltip content. Short, non-interactive text. Mirrors Tooltip content prop.',
97
- label: "Accessible name for the trigger button. Default 'More information'.",
98
- size: 'Info icon size (12/16/20/24px). Maps 1:1 to Icon sizes.',
99
- },
100
- };