@astryxdesign/core 0.2.0-canary.416db07 → 0.2.0-canary.46433cf

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 (108) hide show
  1. package/README.md +3 -3
  2. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  3. package/dist/AvatarGroup/AvatarGroup.js +3 -7
  4. package/dist/Chat/ChatComposerDrawer.js +2 -2
  5. package/dist/ComplexSelector/ComplexSelector.d.ts +108 -0
  6. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -0
  7. package/dist/ComplexSelector/ComplexSelector.js +316 -0
  8. package/dist/ComplexSelector/index.d.ts +7 -0
  9. package/dist/ComplexSelector/index.d.ts.map +1 -0
  10. package/dist/ComplexSelector/index.js +10 -0
  11. package/dist/Lightbox/Lightbox.d.ts +5 -3
  12. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  13. package/dist/Lightbox/Lightbox.js +101 -23
  14. package/dist/Markdown/Markdown.d.ts.map +1 -1
  15. package/dist/Markdown/Markdown.js +31 -9
  16. package/dist/MultiSelector/MultiSelector.d.ts +12 -3
  17. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  18. package/dist/MultiSelector/MultiSelector.js +99 -20
  19. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  20. package/dist/NumberInput/NumberInput.js +15 -4
  21. package/dist/Pagination/Pagination.d.ts.map +1 -1
  22. package/dist/Pagination/Pagination.js +14 -15
  23. package/dist/ProgressBar/ProgressBar.d.ts +48 -2
  24. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  25. package/dist/ProgressBar/ProgressBar.js +79 -8
  26. package/dist/ProgressBar/ProgressBarMarkTooltip.d.ts +27 -0
  27. package/dist/ProgressBar/ProgressBarMarkTooltip.d.ts.map +1 -0
  28. package/dist/ProgressBar/ProgressBarMarkTooltip.js +35 -0
  29. package/dist/ProgressBar/index.d.ts +1 -1
  30. package/dist/ProgressBar/index.d.ts.map +1 -1
  31. package/dist/Selector/Selector.d.ts +11 -2
  32. package/dist/Selector/Selector.d.ts.map +1 -1
  33. package/dist/Selector/Selector.js +99 -20
  34. package/dist/Table/TableCell.js +3 -1
  35. package/dist/Table/TableHeaderCell.js +3 -1
  36. package/dist/Timestamp/Timestamp.d.ts +20 -11
  37. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  38. package/dist/Timestamp/Timestamp.js +51 -59
  39. package/dist/Timestamp/TimestampHoverCard.d.ts +45 -0
  40. package/dist/Timestamp/TimestampHoverCard.d.ts.map +1 -0
  41. package/dist/Timestamp/TimestampHoverCard.js +223 -0
  42. package/dist/Timestamp/tooltipEntries.d.ts +14 -0
  43. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -1
  44. package/dist/Timestamp/tooltipEntries.js +1 -0
  45. package/dist/astryx.css +14 -4
  46. package/dist/astryx.umd.js +50 -50
  47. package/dist/astryx.umd.js.map +4 -4
  48. package/dist/hooks/useGridFocus.d.ts +8 -6
  49. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  50. package/dist/hooks/useListFocus.d.ts +8 -7
  51. package/dist/hooks/useListFocus.d.ts.map +1 -1
  52. package/dist/hooks/useTreeFocus.d.ts.map +1 -1
  53. package/dist/hooks/useTreeFocus.js +13 -2
  54. package/dist/i18n/InternationalizationProvider.d.ts +6 -0
  55. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  56. package/dist/i18n/useDirection.d.ts.map +1 -1
  57. package/dist/i18n/useDirection.js +6 -3
  58. package/dist/index.d.ts +1 -0
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +1 -0
  61. package/locales/en.json +24 -0
  62. package/locales/pseudo.json +18 -0
  63. package/package.json +7 -2
  64. package/src/AvatarGroup/AvatarGroup.tsx +2 -6
  65. package/src/Chat/ChatComposerDrawer.tsx +2 -2
  66. package/src/ComplexSelector/ComplexSelector.doc.mjs +179 -0
  67. package/src/ComplexSelector/ComplexSelector.test.tsx +156 -0
  68. package/src/ComplexSelector/ComplexSelector.tsx +429 -0
  69. package/src/ComplexSelector/index.ts +17 -0
  70. package/src/Lightbox/Lightbox.doc.mjs +2 -2
  71. package/src/Lightbox/Lightbox.test.tsx +178 -0
  72. package/src/Lightbox/Lightbox.tsx +93 -18
  73. package/src/Markdown/Markdown.doc.mjs +80 -0
  74. package/src/Markdown/Markdown.test.tsx +92 -0
  75. package/src/Markdown/Markdown.tsx +93 -65
  76. package/src/MultiSelector/MultiSelector.doc.mjs +32 -7
  77. package/src/MultiSelector/MultiSelector.test.tsx +186 -0
  78. package/src/MultiSelector/MultiSelector.tsx +188 -48
  79. package/src/NumberInput/NumberInput.doc.mjs +0 -3
  80. package/src/NumberInput/NumberInput.test.tsx +38 -3
  81. package/src/NumberInput/NumberInput.tsx +23 -0
  82. package/src/Pagination/Pagination.tsx +12 -10
  83. package/src/ProgressBar/ProgressBar.doc.mjs +15 -0
  84. package/src/ProgressBar/ProgressBar.test.tsx +340 -2
  85. package/src/ProgressBar/ProgressBar.tsx +152 -4
  86. package/src/ProgressBar/ProgressBarMarkTooltip.tsx +43 -0
  87. package/src/ProgressBar/index.ts +5 -1
  88. package/src/Selector/Selector.doc.mjs +25 -5
  89. package/src/Selector/Selector.test.tsx +163 -0
  90. package/src/Selector/Selector.tsx +187 -48
  91. package/src/Table/Table.doc.mjs +2 -2
  92. package/src/Table/Table.test.tsx +27 -0
  93. package/src/Table/TableCell.tsx +1 -1
  94. package/src/Table/TableHeaderCell.tsx +1 -1
  95. package/src/Timestamp/Timestamp.doc.mjs +23 -18
  96. package/src/Timestamp/Timestamp.test.tsx +251 -107
  97. package/src/Timestamp/Timestamp.tsx +71 -93
  98. package/src/Timestamp/TimestampHoverCard.tsx +277 -0
  99. package/src/Timestamp/tooltipEntries.test.ts +9 -0
  100. package/src/Timestamp/tooltipEntries.ts +15 -0
  101. package/src/hooks/useGridFocus.ts +8 -6
  102. package/src/hooks/useListFocus.ts +8 -7
  103. package/src/hooks/useTreeFocus.test.tsx +53 -1
  104. package/src/hooks/useTreeFocus.ts +13 -2
  105. package/src/i18n/InternationalizationProvider.doc.mjs +12 -0
  106. package/src/i18n/InternationalizationProvider.tsx +6 -0
  107. package/src/i18n/useDirection.ts +6 -3
  108. package/src/index.ts +1 -0
package/README.md CHANGED
@@ -80,7 +80,7 @@ Astryx requires **React 19** or later (`react` and `react-dom` >= 19.0.0 are pee
80
80
  Install Astryx and a theme:
81
81
 
82
82
  ```bash
83
- npm install @astryxdesign/core @astryxdesign/theme-neutral
83
+ npm install @astryxdesign/core @astryxdesign/theme-neutral @stylexjs/stylex
84
84
  ```
85
85
 
86
86
  Then pick your setup below based on your framework and styling approach.
@@ -228,7 +228,7 @@ export default function Page() {
228
228
  Use the pre-built dist alongside StyleX for your own styles.
229
229
 
230
230
  ```bash
231
- npm install @astryxdesign/core @astryxdesign/theme-neutral
231
+ npm install @astryxdesign/core @astryxdesign/theme-neutral @stylexjs/stylex
232
232
  ```
233
233
 
234
234
  **`src/app/globals.css`**
@@ -244,7 +244,7 @@ Providers and layout are the same as the Tailwind example (use `@astryxdesign/th
244
244
  ### Vite
245
245
 
246
246
  ```bash
247
- npm install @astryxdesign/core @astryxdesign/theme-neutral
247
+ npm install @astryxdesign/core @astryxdesign/theme-neutral @stylexjs/stylex
248
248
  ```
249
249
 
250
250
  Same CSS imports and providers as above. No build plugins needed; Astryx ships pre-built.
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarGroup.d.ts","sourceRoot":"","sources":["../../src/AvatarGroup/AvatarGroup.tsx"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAA2B,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAC/D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,WAAW,CAAC;AAavD,MAAM,WAAW,gBAAiB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACjE,yCAAyC;IACzC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,QAAQ,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AASD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,IAAW,EACX,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,kBAAkB,EAChC,kBAAkB,EAAE,wBAAwB,EAC5C,SAAS,EACT,OAAO,EACP,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,gBAAgB,GAAG,SAAS,CAuE9B;yBApFe,WAAW"}
1
+ {"version":3,"file":"AvatarGroup.d.ts","sourceRoot":"","sources":["../../src/AvatarGroup/AvatarGroup.tsx"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAA2B,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAC/D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,WAAW,CAAC;AAavD,MAAM,WAAW,gBAAiB,SAAQ,SAAS,CAAC,cAAc,CAAC;IACjE,yCAAyC;IACzC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,QAAQ,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AASD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,IAAW,EACX,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,kBAAkB,EAChC,kBAAkB,EAAE,wBAAwB,EAC5C,SAAS,EACT,OAAO,EACP,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,gBAAgB,GAAG,SAAS,CAmE9B;yBAhFe,WAAW"}
@@ -87,11 +87,9 @@ export function AvatarGroup({
87
87
  }), [size, overlap, numericSize]);
88
88
 
89
89
  // The keyboard hint and roving tab stop only make sense once the group has
90
- // interactive children. Detect their presence (and the writing direction for
91
- // RTL-correct arrow navigation) from the rendered DOM after commit, matching
92
- // how the codebase reads direction elsewhere (`getComputedStyle(el).direction`).
90
+ // interactive children. Detect their presence from the rendered DOM after
91
+ // commit. (Arrow-key direction is auto-detected inside useListFocus.)
93
92
  const [hasInteractiveItems, setHasInteractiveItems] = useState(false);
94
- const [isRtl, setIsRtl] = useState(false);
95
93
 
96
94
  // Single tab stop + roving arrow focus over the group's interactive items.
97
95
  // `itemSelector` targets the shared `[data-avatar-item]` marker stamped on
@@ -105,8 +103,7 @@ export function AvatarGroup({
105
103
  } = useListFocus({
106
104
  itemSelector: '[data-avatar-item]',
107
105
  orientation: 'horizontal',
108
- hasRovingTabIndex: true,
109
- isRtl
106
+ hasRovingTabIndex: true
110
107
  });
111
108
  useIsomorphicLayoutEffect(() => {
112
109
  const root = listRef.current;
@@ -114,7 +111,6 @@ export function AvatarGroup({
114
111
  return;
115
112
  }
116
113
  setHasInteractiveItems(root.querySelector('[data-avatar-item]') != null);
117
- setIsRtl(getComputedStyle(root).direction === 'rtl');
118
114
  });
119
115
  const hintId = useId();
120
116
  const describedBy = [ariaDescribedByFromProps, hasInteractiveItems ? hintId : null].filter(Boolean).join(' ') || undefined;
@@ -142,10 +142,10 @@ export function ChatComposerDrawer({
142
142
  }), /*#__PURE__*/_jsx("div", {
143
143
  ...{
144
144
  0: {
145
- className: "x1ms6mhf xgkxs2y x7v7x1q x1y8v6su xw4jnvo x36qwtl xjspbzw x1q5y3ey x1pe4z86 x1hc1fzr x1e8aii2 xuedmi6 xlr8y92"
145
+ className: "x1ms6mhf xgkxs2y x7v7x1q x1y8v6su xfyiiit x1b2l0oc xjspbzw x1q5y3ey x1pe4z86 x1hc1fzr x1e8aii2 xuedmi6 xlr8y92"
146
146
  },
147
147
  1: {
148
- className: "x1ms6mhf xgkxs2y x7v7x1q x1y8v6su xw4jnvo x36qwtl xjspbzw x1q5y3ey x1pe4z86 x1e8aii2 xuedmi6 xlr8y92 xg01cxk x47corl"
148
+ className: "x1ms6mhf xgkxs2y x7v7x1q x1y8v6su xfyiiit x1b2l0oc xjspbzw x1q5y3ey x1pe4z86 x1e8aii2 xuedmi6 xlr8y92 xg01cxk x47corl"
149
149
  }
150
150
  }[!!isCollapsed << 0]
151
151
  })]
@@ -0,0 +1,108 @@
1
+ /**
2
+ * @file ComplexSelector.tsx
3
+ * @input Uses React, StyleX, Field, usePopover
4
+ * @output Exports ComplexSelector component for custom selector surfaces
5
+ * @position Core implementation; consumed by index.ts
6
+ *
7
+ * SYNC: When modified, update:
8
+ * - /packages/core/src/ComplexSelector/ComplexSelector.doc.mjs
9
+ * - /packages/core/src/ComplexSelector/ComplexSelector.test.tsx
10
+ * - /packages/core/src/ComplexSelector/index.ts
11
+ * - /apps/storybook/stories/ComplexSelector.stories.tsx
12
+ */
13
+ import React, { type ReactNode } from 'react';
14
+ import type { StyleXStyles } from '@stylexjs/stylex';
15
+ import type { BaseProps } from '../BaseProps';
16
+ import { type FieldStatusVariant } from '../Field';
17
+ import type { SizeValue } from '../utils/types';
18
+ export type ComplexSelectorSize = 'sm' | 'md' | 'lg';
19
+ export interface ComplexSelectorRenderState {
20
+ /** Whether the selector surface is open. */
21
+ isOpen: boolean;
22
+ /** Whether changeAction/isLoading is pending. */
23
+ isBusy: boolean;
24
+ /** ID of the trigger button. */
25
+ triggerId: string;
26
+ /** ID of the popup content container. */
27
+ contentId: string;
28
+ }
29
+ export interface ComplexSelectorStatus {
30
+ type: 'warning' | 'error' | 'success';
31
+ message?: string;
32
+ }
33
+ export interface ComplexSelectorProps<Value> extends Omit<BaseProps<HTMLDivElement>, 'children' | 'onChange'> {
34
+ /** Label text for accessibility and the field label. */
35
+ label: string;
36
+ /** Current controlled value. */
37
+ value: Value;
38
+ /** Called when custom content commits a new value. */
39
+ onChange?: (value: Value) => void;
40
+ /** Optional async action after onChange; drives optimistic UI. */
41
+ changeAction?: (value: Value) => void | Promise<void>;
42
+ /** Custom selector surface content rendered inside a dialog popover. */
43
+ children: (value: Value, onChange: (value: Value) => void, close: () => void, state: ComplexSelectorRenderState) => ReactNode;
44
+ /** Label/content shown in the closed trigger. */
45
+ triggerLabel?: ReactNode;
46
+ /** Placeholder shown when triggerLabel is omitted. */
47
+ placeholder?: ReactNode;
48
+ /** Whether to visually hide the field label. */
49
+ isLabelHidden?: boolean;
50
+ /** Helper text displayed below the label. */
51
+ description?: string;
52
+ /** Marks the field optional. */
53
+ isOptional?: boolean;
54
+ /** Marks the field required. */
55
+ isRequired?: boolean;
56
+ /** Disables the selector. */
57
+ isDisabled?: boolean;
58
+ /** Shows loading state on the trigger. */
59
+ isLoading?: boolean;
60
+ /** Validation status. */
61
+ status?: ComplexSelectorStatus;
62
+ /** Status placement. */
63
+ statusVariant?: FieldStatusVariant;
64
+ /** Tooltip text displayed next to the label. */
65
+ labelTooltip?: string;
66
+ /** Trigger and field size. */
67
+ size?: ComplexSelectorSize;
68
+ /** Width of the field. */
69
+ width?: SizeValue;
70
+ /** Popup placement. */
71
+ placement?: 'above' | 'below' | 'start' | 'end';
72
+ /** StyleX styles for the popup content container. */
73
+ contentXstyle?: StyleXStyles;
74
+ /** Test ID for the trigger container. */
75
+ 'data-testid'?: string;
76
+ }
77
+ /**
78
+ * A selector shell for rich, custom selection surfaces.
79
+ *
80
+ * ComplexSelector owns the field, trigger, popover, focus restore, and async
81
+ * change action flow. Consumers provide the dialog content as a render function,
82
+ * using the supplied `value`, `onChange`, and `close` helpers to compose the
83
+ * right accessible structure for the custom selector.
84
+ *
85
+ * @example
86
+ * ```
87
+ * <ComplexSelector
88
+ * label="Fruit"
89
+ * value={value}
90
+ * onChange={setValue}
91
+ * triggerLabel={`${value.fruit} ${value.ripeness}`}>
92
+ * {(value, onChange, close) => (
93
+ * <FruitGrid
94
+ * value={value}
95
+ * onChange={nextValue => {
96
+ * onChange(nextValue);
97
+ * close();
98
+ * }}
99
+ * />
100
+ * )}
101
+ * </ComplexSelector>
102
+ * ```
103
+ */
104
+ export declare function ComplexSelector<Value>({ label, value, onChange, changeAction, children, triggerLabel, placeholder: placeholderFromProps, isLabelHidden, description, isOptional, isRequired, isDisabled, isLoading, status, statusVariant, labelTooltip, size, width, placement, contentXstyle, xstyle, className, style, 'data-testid': testId, ...props }: ComplexSelectorProps<Value>): React.JSX.Element;
105
+ export declare namespace ComplexSelector {
106
+ var displayName: string;
107
+ }
108
+ //# sourceMappingURL=ComplexSelector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComplexSelector.d.ts","sourceRoot":"","sources":["../../src/ComplexSelector/ComplexSelector.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,316 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file ComplexSelector.tsx
7
+ * @input Uses React, StyleX, Field, usePopover
8
+ * @output Exports ComplexSelector component for custom selector surfaces
9
+ * @position Core implementation; consumed by index.ts
10
+ *
11
+ * SYNC: When modified, update:
12
+ * - /packages/core/src/ComplexSelector/ComplexSelector.doc.mjs
13
+ * - /packages/core/src/ComplexSelector/ComplexSelector.test.tsx
14
+ * - /packages/core/src/ComplexSelector/index.ts
15
+ * - /apps/storybook/stories/ComplexSelector.stories.tsx
16
+ */
17
+ import React, { useCallback, useId, useOptimistic, useTransition } from 'react';
18
+ import * as stylex from '@stylexjs/stylex';
19
+ import { Field, inputWrapperStyles } from "../Field/index.js";
20
+ import { Icon } from "../Icon/index.js";
21
+ import { Spinner } from "../Spinner/index.js";
22
+ import { useTranslator } from "../i18n/index.js";
23
+ import { layerAnimations } from "../Layer/layerAnimations.stylex.js";
24
+ import { usePopover } from "../Popover/usePopover.js";
25
+ import "../theme/tokens.stylex.js";
26
+ import { colorVars, durationVars, easeVars, radiusVars, sizeVars, spacingVars, typographyVars, typeScaleVars } from "../theme/tokens.stylex.js";
27
+ import { mergeProps } from "../utils/index.js";
28
+ import { themeProps } from "../utils/themeProps.js";
29
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
30
+ const styles = {
31
+ triggerContainer: {
32
+ kVAEAm: "x1n2onr6",
33
+ k1xSpc: "x78zum5",
34
+ kGNEyG: "x6s0dn4",
35
+ kjj79g: "x1qughib",
36
+ kOIVth: "x1txdalj",
37
+ kzqmXN: "xh8yej3",
38
+ k8WAf4: "xce4md1",
39
+ kg3NbH: "xrrkdod",
40
+ kMv6JI: "x9ynric",
41
+ kGuDYH: "xcr08ib xdk3u5w",
42
+ kLWn49: "x1kq96og",
43
+ kMwMTN: "x1tgivj0",
44
+ kkrTdU: "x1ypdohk",
45
+ $$css: true
46
+ },
47
+ trigger: {
48
+ k1xSpc: "x78zum5",
49
+ kGNEyG: "x6s0dn4",
50
+ kjj79g: "x1qughib",
51
+ kOIVth: "x1txdalj",
52
+ kzQI83: "x1iyjqo2",
53
+ kmuXW: "xs83m0k",
54
+ kCS8Yb: "x1r8uery",
55
+ k7Eaqz: "xeuugli",
56
+ kmVPX3: "x1717udv",
57
+ kogj98: "x1ghz6dp",
58
+ kMzoRj: "xc342km",
59
+ ksu8eU: "xng3xce",
60
+ kWkggS: "xjbqb8w",
61
+ kMv6JI: "xjb2p0i",
62
+ kGuDYH: "x1qlqyl8",
63
+ kLWn49: "x15bjb6t",
64
+ kMwMTN: "x1heor9g",
65
+ kkrTdU: "x1ypdohk",
66
+ kI3sdo: "x1a2a7pz",
67
+ kaIpWk: "xh6dtrn",
68
+ $$css: true
69
+ },
70
+ triggerText: {
71
+ kzQI83: "x1iyjqo2",
72
+ k7Eaqz: "xeuugli",
73
+ kVQacm: "xb3r6kr",
74
+ kg5iWk: "xlyipyv",
75
+ khDVqt: "xuxw1ft",
76
+ k9WMMc: "x1yc453h",
77
+ $$css: true
78
+ },
79
+ placeholder: {
80
+ kMwMTN: "xv1l7n4",
81
+ $$css: true
82
+ },
83
+ triggerIcon: {
84
+ kmuXW: "x2lah0s",
85
+ k1xSpc: "x78zum5",
86
+ kGNEyG: "x6s0dn4",
87
+ kjj79g: "xl56j7k",
88
+ kzqmXN: "x1kky2od",
89
+ kZKoxP: "xlup9mm",
90
+ k1ekBW: "x11xpdln",
91
+ kIyJzY: "xuedmi6",
92
+ kAMwcw: "xlr8y92",
93
+ k3nNDw: "x1g0ag68",
94
+ kMwMTN: "xv9yike",
95
+ $$css: true
96
+ },
97
+ triggerIconOpen: {
98
+ k3aq6I: "x19jd1h0",
99
+ $$css: true
100
+ },
101
+ popover: {
102
+ k7Eaqz: "xrzjruh",
103
+ keoZOQ: "xcsaf9d",
104
+ $$css: true
105
+ },
106
+ content: {
107
+ kB7OPa: "x9f619",
108
+ kskxy: "xpnbb6b",
109
+ kVQacm: "xysyzu8",
110
+ kmVPX3: "x1b2ylru",
111
+ $$css: true
112
+ },
113
+ sm: {
114
+ kAzted: "x1gdqauq",
115
+ $$css: true
116
+ },
117
+ md: {
118
+ kAzted: "x1uogy3y",
119
+ $$css: true
120
+ },
121
+ lg: {
122
+ kAzted: "x1xf4nls",
123
+ $$css: true
124
+ },
125
+ disabled: {
126
+ kkrTdU: "x1h6gzvc",
127
+ $$css: true
128
+ },
129
+ focusRing: {
130
+ k8PahR: "x1oqel4m",
131
+ kfQpEX: "x1fanpfn",
132
+ $$css: true
133
+ }
134
+ };
135
+ /**
136
+ * A selector shell for rich, custom selection surfaces.
137
+ *
138
+ * ComplexSelector owns the field, trigger, popover, focus restore, and async
139
+ * change action flow. Consumers provide the dialog content as a render function,
140
+ * using the supplied `value`, `onChange`, and `close` helpers to compose the
141
+ * right accessible structure for the custom selector.
142
+ *
143
+ * @example
144
+ * ```
145
+ * <ComplexSelector
146
+ * label="Fruit"
147
+ * value={value}
148
+ * onChange={setValue}
149
+ * triggerLabel={`${value.fruit} ${value.ripeness}`}>
150
+ * {(value, onChange, close) => (
151
+ * <FruitGrid
152
+ * value={value}
153
+ * onChange={nextValue => {
154
+ * onChange(nextValue);
155
+ * close();
156
+ * }}
157
+ * />
158
+ * )}
159
+ * </ComplexSelector>
160
+ * ```
161
+ */
162
+ export function ComplexSelector({
163
+ label,
164
+ value,
165
+ onChange,
166
+ changeAction,
167
+ children,
168
+ triggerLabel,
169
+ placeholder: placeholderFromProps,
170
+ isLabelHidden = false,
171
+ description,
172
+ isOptional = false,
173
+ isRequired = false,
174
+ isDisabled = false,
175
+ isLoading = false,
176
+ status,
177
+ statusVariant = 'attached',
178
+ labelTooltip,
179
+ size = 'md',
180
+ width,
181
+ placement = 'below',
182
+ contentXstyle,
183
+ xstyle,
184
+ className,
185
+ style,
186
+ 'data-testid': testId,
187
+ ...props
188
+ }) {
189
+ const t = useTranslator();
190
+ const placeholder = placeholderFromProps ?? t('@astryx.selector.placeholder');
191
+ const triggerId = useId();
192
+ const labelId = useId();
193
+ const contentId = useId();
194
+ const descriptionId = useId();
195
+ const statusMessageId = useId();
196
+ const ariaDescribedBy = [description ? descriptionId : null, status?.message ? statusMessageId : null].filter(id => id != null).join(' ') || undefined;
197
+ const [isPending, startTransition] = useTransition();
198
+ const [optimisticValue, setOptimisticValue] = useOptimistic(value);
199
+ const isBusy = isLoading || isPending;
200
+ const popover = usePopover({
201
+ dialogLabel: label,
202
+ hasCloseButton: false,
203
+ hasAutoFocus: true,
204
+ onHide: () => {
205
+ document.getElementById(triggerId)?.focus();
206
+ }
207
+ });
208
+ const commitValue = useCallback(nextValue => {
209
+ onChange?.(nextValue);
210
+ if (changeAction) {
211
+ startTransition(async () => {
212
+ setOptimisticValue(nextValue);
213
+ await changeAction(nextValue);
214
+ });
215
+ }
216
+ }, [changeAction, onChange, setOptimisticValue, startTransition]);
217
+ const triggerContent = triggerLabel ?? placeholder;
218
+ const content = /*#__PURE__*/_jsx("div", {
219
+ id: contentId,
220
+ ...stylex.props(styles.content, contentXstyle),
221
+ children: children(optimisticValue, commitValue, popover.hide, {
222
+ isOpen: popover.isOpen,
223
+ isBusy,
224
+ triggerId,
225
+ contentId
226
+ })
227
+ });
228
+ const selectorContent = /*#__PURE__*/_jsxs(_Fragment, {
229
+ children: [/*#__PURE__*/_jsxs("div", {
230
+ ref: popover.triggerRef,
231
+ "data-testid": testId,
232
+ ...props,
233
+ onClick: () => {
234
+ if (!isDisabled) {
235
+ popover.toggle();
236
+ }
237
+ },
238
+ ...mergeProps(themeProps('complex-selector', {
239
+ size,
240
+ status: status?.type ?? null
241
+ }), stylex.props(inputWrapperStyles.base, styles.triggerContainer, styles[size], styles.focusRing, isDisabled && inputWrapperStyles.disabled, isDisabled && styles.disabled, triggerLabel == null && styles.placeholder, xstyle), className, style),
242
+ children: [/*#__PURE__*/_jsx("button", {
243
+ id: triggerId,
244
+ type: "button",
245
+ "aria-haspopup": "dialog",
246
+ "aria-expanded": popover.isOpen,
247
+ "aria-controls": contentId,
248
+ "aria-describedby": ariaDescribedBy,
249
+ "aria-labelledby": labelId,
250
+ "aria-required": isRequired ? 'true' : undefined,
251
+ "aria-invalid": status?.type === 'error' ? 'true' : undefined,
252
+ "aria-busy": isBusy || undefined,
253
+ disabled: isDisabled,
254
+ onKeyDown: event => {
255
+ if (event.key === 'ArrowDown' && !popover.isOpen && !isDisabled) {
256
+ event.preventDefault();
257
+ popover.show();
258
+ }
259
+ },
260
+ ...{
261
+ className: "x78zum5 x6s0dn4 x1qughib x1txdalj x1iyjqo2 xs83m0k x1r8uery xeuugli x1717udv x1ghz6dp xc342km xng3xce xjbqb8w xjb2p0i x1qlqyl8 x15bjb6t x1heor9g x1ypdohk x1a2a7pz xh6dtrn"
262
+ },
263
+ children: /*#__PURE__*/_jsx("span", {
264
+ ...{
265
+ className: "x1iyjqo2 xeuugli xb3r6kr xlyipyv xuxw1ft x1yc453h"
266
+ },
267
+ children: triggerContent
268
+ })
269
+ }), isBusy && /*#__PURE__*/_jsx(Spinner, {
270
+ size: "sm"
271
+ }), /*#__PURE__*/_jsx("span", {
272
+ ...{
273
+ 0: {
274
+ className: "x2lah0s x78zum5 x6s0dn4 xl56j7k x1kky2od xlup9mm x11xpdln xuedmi6 xlr8y92 x1g0ag68 xv9yike"
275
+ },
276
+ 1: {
277
+ className: "x2lah0s x78zum5 x6s0dn4 xl56j7k x1kky2od xlup9mm x11xpdln xuedmi6 xlr8y92 x1g0ag68 xv9yike x19jd1h0"
278
+ }
279
+ }[!!popover.isOpen << 0],
280
+ children: /*#__PURE__*/_jsx(Icon, {
281
+ icon: "chevronDown",
282
+ size: "sm",
283
+ color: "inherit",
284
+ ...themeProps('complex-selector-indicator-icon', {
285
+ state: popover.isOpen ? 'expanded' : 'collapsed'
286
+ })
287
+ })
288
+ })]
289
+ }), popover.render(content, {
290
+ placement,
291
+ alignment: 'start',
292
+ xstyle: [styles.popover, layerAnimations[placement]]
293
+ })]
294
+ });
295
+ return /*#__PURE__*/_jsx(Field, {
296
+ label: label,
297
+ isLabelHidden: isLabelHidden,
298
+ description: description,
299
+ inputID: triggerId,
300
+ descriptionID: description ? descriptionId : undefined,
301
+ labelID: labelId,
302
+ isOptional: isOptional,
303
+ isRequired: isRequired,
304
+ isDisabled: isDisabled,
305
+ status: status ? {
306
+ type: status.type,
307
+ message: status.message,
308
+ messageID: status.message ? statusMessageId : undefined
309
+ } : undefined,
310
+ statusVariant: statusVariant,
311
+ labelTooltip: labelTooltip,
312
+ width: width,
313
+ children: selectorContent
314
+ });
315
+ }
316
+ ComplexSelector.displayName = 'ComplexSelector';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @file index.ts
3
+ * @output Exports ComplexSelector and types
4
+ * @position Public API entry point
5
+ */
6
+ export { ComplexSelector, type ComplexSelectorProps, type ComplexSelectorRenderState, type ComplexSelectorSize, type ComplexSelectorStatus, } from './ComplexSelector';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ComplexSelector/index.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,GAC3B,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,10 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file index.ts
7
+ * @output Exports ComplexSelector and types
8
+ * @position Public API entry point
9
+ */
10
+ export { ComplexSelector } from "./ComplexSelector.js";
@@ -65,8 +65,8 @@ export interface LightboxProps extends BaseProps<HTMLDialogElement> {
65
65
  */
66
66
  onIndexChange?: (index: number) => void;
67
67
  /**
68
- * Enable zoom on double-click (images only).
69
- * When zoomed, drag to pan.
68
+ * Enable zoom on double-click, or Enter/Space/`+`/`-` via keyboard
69
+ * (images only). When zoomed, drag or use arrow keys to pan.
70
70
  * @default false
71
71
  */
72
72
  hasZoom?: boolean;
@@ -81,7 +81,9 @@ export interface LightboxProps extends BaseProps<HTMLDialogElement> {
81
81
  *
82
82
  * Supports single image and gallery modes. In gallery mode, provides
83
83
  * prev/next navigation via buttons and arrow keys. Optionally supports
84
- * zoom (double-click to toggle 2x) and pan (drag when zoomed).
84
+ * zoom (double-click, Enter/Space on the image, or `+`/`-` to toggle 2x)
85
+ * and pan (drag or arrow keys when zoomed; arrows navigate the gallery
86
+ * when not zoomed).
85
87
  *
86
88
  * Uses the native `<dialog>` element with `showModal()` for focus
87
89
  * trapping and top-layer placement. Dismiss via Escape, close button,
@@ -1 +1 @@
1
- {"version":3,"file":"Lightbox.d.ts","sourceRoot":"","sources":["../../src/Lightbox/Lightbox.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AASf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAI5C;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,OAAO,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,uBAAuB;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,GAAG,EAAE,MAAM,CAAC;IACZ,iDAAiD;IACjD,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,iBAAiB,CAAC;CAC1B;AAED,MAAM,WAAW,aAAc,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IACjE,+CAA+C;IAC/C,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,KAAK,EAAE,aAAa,GAAG,aAAa,EAAE,CAAC;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAqID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,QAAQ,CAAC,EACvB,MAAM,EACN,YAAY,EACZ,KAAK,EACL,KAAK,EAAE,eAAe,EACtB,YAAgB,EAChB,aAAa,EACb,OAAe,EACf,WAAmB,EACnB,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,aAAa,EACxB,GAAG,KAAK,EACT,EAAE,aAAa,sCAwVf;yBAxWe,QAAQ"}
1
+ {"version":3,"file":"Lightbox.d.ts","sourceRoot":"","sources":["../../src/Lightbox/Lightbox.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AASf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAI5C;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,OAAO,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,uBAAuB;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,GAAG,EAAE,MAAM,CAAC;IACZ,iDAAiD;IACjD,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,iBAAiB,CAAC;CAC1B;AAED,MAAM,WAAW,aAAc,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IACjE,+CAA+C;IAC/C,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,KAAK,EAAE,aAAa,GAAG,aAAa,EAAE,CAAC;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA6JD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,QAAQ,CAAC,EACvB,MAAM,EACN,YAAY,EACZ,KAAK,EACL,KAAK,EAAE,eAAe,EACtB,YAAgB,EAChB,aAAa,EACb,OAAe,EACf,WAAmB,EACnB,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,aAAa,EACxB,GAAG,KAAK,EACT,EAAE,aAAa,sCAyYf;yBAzZe,QAAQ"}