@astryxdesign/core 0.7.7-canary.f9ac85f → 0.7.7
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.
- package/CHANGELOG.md +104 -0
- package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts +13 -0
- package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts.map +1 -0
- package/dist/AppShell/useCloseMobileDrawerOnActivate.js +45 -0
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +79 -9
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +1 -1
- package/dist/BottomSheet/bottomSheetStack.d.ts.map +1 -1
- package/dist/BottomSheet/bottomSheetStack.js +5 -4
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts +2 -1
- package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
- package/dist/Breadcrumbs/BreadcrumbItem.js +7 -3
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +30 -2
- package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxList.js +7 -1
- package/dist/CheckboxList/CheckboxListContext.d.ts +6 -0
- package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxListItem.js +10 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +5 -2
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +13 -27
- package/dist/DateInput/Wheel.d.ts.map +1 -1
- package/dist/DateInput/Wheel.js +22 -41
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +5 -2
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +4 -3
- package/dist/Drawer/Drawer.js +0 -1
- package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
- package/dist/Drawer/useDrawerDialogPresence.js +5 -1
- package/dist/Field/PanelSearchInput.d.ts +9 -1
- package/dist/Field/PanelSearchInput.d.ts.map +1 -1
- package/dist/Field/PanelSearchInput.js +3 -1
- package/dist/InfoTip/InfoTip.d.ts +2 -2
- package/dist/InfoTip/InfoTip.js +4 -4
- package/dist/Layer/layerAnimations.stylex.d.ts +7 -8
- package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
- package/dist/Layer/layerAnimations.stylex.js +25 -20
- package/dist/Layer/usePressPin.d.ts +3 -5
- package/dist/Layer/usePressPin.d.ts.map +1 -1
- package/dist/Layer/usePressPin.js +4 -6
- package/dist/MobileNav/MobileNav.js +0 -1
- package/dist/MultiSelector/hooks.d.ts.map +1 -1
- package/dist/MultiSelector/hooks.js +2 -4
- package/dist/Outline/Outline.d.ts +2 -4
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +4 -52
- package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
- package/dist/Outline/useOutlineFromMarkdown.js +1 -0
- package/dist/Outline/useScrollSpy.js +3 -3
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +134 -179
- package/dist/RadioList/RadioList.d.ts.map +1 -1
- package/dist/RadioList/RadioList.js +5 -0
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +118 -260
- package/dist/Resizable/resizeGesture.d.ts +91 -0
- package/dist/Resizable/resizeGesture.d.ts.map +1 -0
- package/dist/Resizable/resizeGesture.js +251 -0
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +3 -9
- package/dist/Token/Token.d.ts +2 -2
- package/dist/Token/Token.d.ts.map +1 -1
- package/dist/Token/Token.js +12 -4
- package/dist/Token/TokenNamingContext.d.ts +14 -0
- package/dist/Token/TokenNamingContext.d.ts.map +1 -0
- package/dist/Token/TokenNamingContext.js +23 -0
- package/dist/Tokenizer/Tokenizer.d.ts +46 -59
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +35 -58
- package/dist/Tokenizer/TokenizerBottomSheet.d.ts +25 -28
- package/dist/Tokenizer/TokenizerBottomSheet.d.ts.map +1 -1
- package/dist/Tokenizer/TokenizerBottomSheet.js +157 -54
- package/dist/Tokenizer/TokenizerItemRemovableContext.d.ts.map +1 -1
- package/dist/Tokenizer/TokenizerItemRemovableContext.js +5 -3
- package/dist/Tooltip/Tooltip.d.ts +25 -30
- package/dist/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip/Tooltip.js +24 -5
- package/dist/Tooltip/index.d.ts +1 -1
- package/dist/Tooltip/index.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.d.ts +28 -29
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +20 -8
- package/dist/TopNav/TopNavItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavItem.js +4 -7
- package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
- package/dist/TopNav/TopNavMegaMenuItem.js +4 -7
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +4 -7
- package/dist/astryx.css +5 -5
- package/dist/gestures/GestureHost.d.ts +27 -1
- package/dist/gestures/GestureHost.d.ts.map +1 -1
- package/dist/gestures/GestureHost.js +90 -8
- package/dist/hooks/useHighlightedOptionScroll.d.ts +11 -1
- package/dist/hooks/useHighlightedOptionScroll.d.ts.map +1 -1
- package/dist/hooks/useHighlightedOptionScroll.js +103 -5
- package/dist/hooks/useMenuPress.d.ts.map +1 -1
- package/dist/hooks/useMenuPress.js +4 -2
- package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.d.ts.map +1 -1
- package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.js +1 -0
- package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.d.ts.map +1 -1
- package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.js +7 -1
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +1 -0
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +1 -0
- package/dist/internal/SheetConfirmation.d.ts +11 -0
- package/dist/internal/SheetConfirmation.d.ts.map +1 -0
- package/dist/internal/SheetConfirmation.js +92 -0
- package/dist/internal/sheetDraft.d.ts +35 -0
- package/dist/internal/sheetDraft.d.ts.map +1 -0
- package/dist/internal/sheetDraft.js +65 -0
- package/dist/internal/touch-sheet.d.ts +21 -0
- package/dist/internal/touch-sheet.d.ts.map +1 -0
- package/dist/internal/touch-sheet.js +19 -0
- package/dist/internal/useDismissSearchOnTouchDrag.d.ts +24 -0
- package/dist/internal/useDismissSearchOnTouchDrag.d.ts.map +1 -0
- package/dist/internal/useDismissSearchOnTouchDrag.js +56 -0
- package/dist/motion/reducedMotion.d.ts +24 -0
- package/dist/motion/reducedMotion.d.ts.map +1 -1
- package/dist/motion/reducedMotion.js +78 -1
- package/dist/utils/clickGuard.d.ts +8 -1
- package/dist/utils/clickGuard.d.ts.map +1 -1
- package/dist/utils/clickGuard.js +24 -1
- package/locales/en.json +4 -0
- package/locales/pseudo.json +3 -0
- package/package.json +9 -9
- package/src/AppShell/useCloseMobileDrawerOnActivate.ts +52 -0
- package/src/BottomSheet/BottomSheet.tsx +95 -10
- package/src/BottomSheet/BottomSheetPanel.tsx +7 -2
- package/src/BottomSheet/bottomSheetStack.ts +8 -4
- package/src/Breadcrumbs/BreadcrumbItem.tsx +13 -3
- package/src/Button/Button.tsx +37 -1
- package/src/CheckboxList/CheckboxList.doc.mjs +4 -2
- package/src/CheckboxList/CheckboxList.tsx +6 -0
- package/src/CheckboxList/CheckboxListContext.tsx +6 -0
- package/src/CheckboxList/CheckboxListItem.tsx +11 -2
- package/src/DateInput/DateInput.tsx +8 -2
- package/src/DateInput/MonthScroller.tsx +18 -22
- package/src/DateInput/Wheel.tsx +30 -32
- package/src/DateTimeInput/DateTimeInput.tsx +8 -2
- package/src/Dialog/Dialog.tsx +19 -4
- package/src/Drawer/Drawer.tsx +1 -1
- package/src/Drawer/useDrawerDialogPresence.ts +5 -1
- package/src/Field/PanelSearchInput.tsx +11 -0
- package/src/InfoTip/InfoTip.doc.mjs +3 -3
- package/src/InfoTip/InfoTip.tsx +5 -5
- package/src/Layer/layerAnimations.stylex.ts +24 -16
- package/src/Layer/usePressPin.ts +5 -11
- package/src/MobileNav/MobileNav.tsx +1 -1
- package/src/MultiSelector/hooks.ts +2 -4
- package/src/Outline/Outline.doc.mjs +177 -51
- package/src/Outline/Outline.tsx +5 -57
- package/src/Outline/useOutlineFromMarkdown.doc.mjs +157 -0
- package/src/Outline/useOutlineFromMarkdown.ts +1 -0
- package/src/Outline/useScrollSpy.ts +3 -3
- package/src/PowerSearch/PowerSearch.doc.mjs +3 -3
- package/src/PowerSearch/PowerSearch.tsx +196 -183
- package/src/RadioList/RadioList.doc.mjs +4 -2
- package/src/RadioList/RadioList.tsx +4 -0
- package/src/Resizable/ResizeHandle.tsx +133 -328
- package/src/Resizable/resizeGesture.ts +368 -0
- package/src/SideNav/SideNavItem.tsx +3 -7
- package/src/Token/Token.tsx +13 -4
- package/src/Token/TokenNamingContext.ts +28 -0
- package/src/Tokenizer/Tokenizer.doc.mjs +16 -0
- package/src/Tokenizer/Tokenizer.tsx +81 -86
- package/src/Tokenizer/TokenizerBottomSheet.tsx +219 -93
- package/src/Tokenizer/TokenizerItemRemovableContext.ts +5 -3
- package/src/Tooltip/Tooltip.doc.mjs +123 -54
- package/src/Tooltip/Tooltip.tsx +36 -39
- package/src/Tooltip/index.ts +0 -1
- package/src/Tooltip/useTooltip.doc.mjs +147 -33
- package/src/Tooltip/useTooltip.tsx +39 -32
- package/src/TopNav/TopNavItem.tsx +6 -5
- package/src/TopNav/TopNavMegaMenuItem.tsx +6 -5
- package/src/TopNav/TopNavMenu.tsx +6 -5
- package/src/gestures/GestureHost.ts +105 -10
- package/src/hooks/useHighlightedOptionScroll.ts +113 -9
- package/src/hooks/useMenuPress.ts +8 -2
- package/src/i18n/generated-english/PowerSearch/PowerSearch.generated.ts +1 -0
- package/src/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.ts +7 -1
- package/src/i18n/generated-locales/en.generated.ts +1 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +1 -0
- package/src/internal/SheetConfirmation.tsx +101 -0
- package/src/internal/sheetDraft.ts +80 -0
- package/src/internal/touch-sheet.ts +21 -0
- package/src/internal/useDismissSearchOnTouchDrag.ts +69 -0
- package/src/motion/reducedMotion.ts +79 -1
- package/src/utils/clickGuard.ts +37 -1
- package/dist/internal/tokenizer-presentation.d.ts +0 -21
- package/dist/internal/tokenizer-presentation.d.ts.map +0 -1
- package/dist/internal/tokenizer-presentation.js +0 -21
- package/src/internal/tokenizer-presentation.ts +0 -25
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file resizeGesture.ts
|
|
5
|
+
* @input A handle element, a resize model (its state, and a gesture's start,
|
|
6
|
+
* move, end and cancel), and the handle's axis, reversal and disabled state
|
|
7
|
+
* @output createResizeGesture: the window splitter's input in one place: a
|
|
8
|
+
* pointer drag that holds its pointer, the arrow keys, Home, End and Enter,
|
|
9
|
+
* and a double-click collapse; and the body's drag cursor
|
|
10
|
+
* @position Internal to the Resizable family. ResizeHandle forwards its React
|
|
11
|
+
* events to it. No React, and no DOM work on import
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type {ResizableDirection} from './resizableModel';
|
|
15
|
+
|
|
16
|
+
/** Pixels an arrow key moves the separator; Shift moves it further. */
|
|
17
|
+
export const KEYBOARD_STEP = 10;
|
|
18
|
+
export const KEYBOARD_LARGE_STEP = 50;
|
|
19
|
+
|
|
20
|
+
/** What a gesture reads from its region when an input arrives. */
|
|
21
|
+
export interface ResizeGestureState {
|
|
22
|
+
/** The region's size in px, 0 while collapsed. */
|
|
23
|
+
readonly size: number;
|
|
24
|
+
readonly isCollapsed: boolean;
|
|
25
|
+
readonly minPx: number;
|
|
26
|
+
/** The configured maximum, Infinity when the region has none. */
|
|
27
|
+
readonly maxPx: number;
|
|
28
|
+
readonly snaps: ReadonlyArray<number>;
|
|
29
|
+
readonly collapsedSize: number;
|
|
30
|
+
readonly collapsible: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The region a gesture resizes. */
|
|
34
|
+
export interface ResizeGestureModel {
|
|
35
|
+
state(): ResizeGestureState;
|
|
36
|
+
/** A gesture begins: the region holds its basis until it ends. */
|
|
37
|
+
start(): void;
|
|
38
|
+
/** The gesture has moved `delta` px from where it began. */
|
|
39
|
+
move(delta: number): void;
|
|
40
|
+
/** The gesture ends as completed. */
|
|
41
|
+
end(): void;
|
|
42
|
+
/** The gesture ends without completing. */
|
|
43
|
+
cancel(): void;
|
|
44
|
+
/**
|
|
45
|
+
* The region's own collapse and expand. They restore the size it had before
|
|
46
|
+
* it collapsed, as the APG window splitter's Enter does. Without them a
|
|
47
|
+
* toggle is a resize gesture to the minimum.
|
|
48
|
+
*/
|
|
49
|
+
collapse?: () => void;
|
|
50
|
+
expand?: () => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** The handle's configuration, read at each input. */
|
|
54
|
+
export interface ResizeGestureConfig {
|
|
55
|
+
readonly direction: ResizableDirection;
|
|
56
|
+
readonly isReversed: boolean;
|
|
57
|
+
readonly isDisabled: boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The fields of a pointer event the gesture reads (native or React). */
|
|
61
|
+
export interface ResizeGesturePointerEvent {
|
|
62
|
+
readonly pointerId: number;
|
|
63
|
+
readonly clientX: number;
|
|
64
|
+
readonly clientY: number;
|
|
65
|
+
readonly currentTarget: EventTarget | null;
|
|
66
|
+
preventDefault(): void;
|
|
67
|
+
stopPropagation(): void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The fields of a key event the gesture reads (native or React). */
|
|
71
|
+
export interface ResizeGestureKeyEvent {
|
|
72
|
+
readonly key: string;
|
|
73
|
+
readonly shiftKey: boolean;
|
|
74
|
+
preventDefault(): void;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ResizeGesture {
|
|
78
|
+
pointerDown(event: ResizeGesturePointerEvent): void;
|
|
79
|
+
pointerMove(event: ResizeGesturePointerEvent): void;
|
|
80
|
+
pointerUp(event: ResizeGesturePointerEvent): void;
|
|
81
|
+
/** A `pointercancel` or a `lostpointercapture`. */
|
|
82
|
+
pointerCancel(event: ResizeGesturePointerEvent): void;
|
|
83
|
+
keyDown(event: ResizeGestureKeyEvent): void;
|
|
84
|
+
doubleClick(): void;
|
|
85
|
+
/** Whether a pointer drag is in flight. */
|
|
86
|
+
readonly isDragging: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Ends a drag in flight as cancelled, for a handle that goes away mid-drag.
|
|
89
|
+
* It releases the pointer and the body's drag cursor, and tells the region
|
|
90
|
+
* that the gesture is over.
|
|
91
|
+
*/
|
|
92
|
+
interrupt(): void;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Drag feedback lives on the page's `<body>`, not on the handle. During a
|
|
97
|
+
* drag the pointer moves over whatever the panels contain, and the resize
|
|
98
|
+
* cursor must hold there.
|
|
99
|
+
*/
|
|
100
|
+
function applyBodyDragStyles(isHorizontal: boolean): void {
|
|
101
|
+
document.body.style.cursor = isHorizontal ? 'col-resize' : 'row-resize';
|
|
102
|
+
document.body.style.userSelect = 'none';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function clearBodyDragStyles(): void {
|
|
106
|
+
document.body.style.cursor = '';
|
|
107
|
+
document.body.style.userSelect = '';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The snap point `count` points away from the region's current size in
|
|
112
|
+
* `direction` (+1 grows, -1 shrinks). A point beyond the minimum or the
|
|
113
|
+
* maximum counts as that bound. When fewer points remain, the nearest end
|
|
114
|
+
* point serves, or the current size when the region is already there.
|
|
115
|
+
*/
|
|
116
|
+
function adjacentSnapPoint(
|
|
117
|
+
state: ResizeGestureState,
|
|
118
|
+
direction: 1 | -1,
|
|
119
|
+
count: number,
|
|
120
|
+
): number {
|
|
121
|
+
const points = [
|
|
122
|
+
...new Set(
|
|
123
|
+
state.snaps.map(p => Math.min(state.maxPx, Math.max(state.minPx, p))),
|
|
124
|
+
),
|
|
125
|
+
].sort((a, b) => a - b);
|
|
126
|
+
const {size} = state;
|
|
127
|
+
const ahead =
|
|
128
|
+
direction > 0
|
|
129
|
+
? points.filter(p => p > size)
|
|
130
|
+
: points.filter(p => p < size).reverse();
|
|
131
|
+
if (ahead.length === 0) {
|
|
132
|
+
return size;
|
|
133
|
+
}
|
|
134
|
+
return ahead[Math.min(count, ahead.length) - 1];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function step(model: ResizeGestureModel, delta: number): void {
|
|
138
|
+
model.start();
|
|
139
|
+
model.move(delta);
|
|
140
|
+
model.end();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Collapse an open pane, or restore a collapsed one to the size it had before
|
|
145
|
+
* it collapsed, with the region's own collapse and expand. A region without
|
|
146
|
+
* them falls back to a resize gesture to the minimum. That gesture cannot
|
|
147
|
+
* reopen a pane whose minimum sits below its collapse threshold.
|
|
148
|
+
*/
|
|
149
|
+
function toggleCollapse(model: ResizeGestureModel): void {
|
|
150
|
+
const state = model.state();
|
|
151
|
+
if (model.collapse != null && model.expand != null) {
|
|
152
|
+
if (state.isCollapsed) {
|
|
153
|
+
model.expand();
|
|
154
|
+
} else {
|
|
155
|
+
model.collapse();
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
step(model, state.isCollapsed ? state.minPx : -state.size);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The window splitter's input for one handle. A drag takes pointer capture
|
|
164
|
+
* on the element the press landed on. The rest of the gesture then goes to
|
|
165
|
+
* that element. Without capture, the browser hit-tests each move, and an
|
|
166
|
+
* embedded frame under the pointer takes the drag.
|
|
167
|
+
*/
|
|
168
|
+
export function createResizeGesture(
|
|
169
|
+
getHandle: () => Element | null,
|
|
170
|
+
model: ResizeGestureModel,
|
|
171
|
+
getConfig: () => ResizeGestureConfig,
|
|
172
|
+
onDraggingChange?: (isDragging: boolean) => void,
|
|
173
|
+
): ResizeGesture {
|
|
174
|
+
let drag: {
|
|
175
|
+
pointerId: number;
|
|
176
|
+
startPos: number;
|
|
177
|
+
rtl: number;
|
|
178
|
+
target: Element | null;
|
|
179
|
+
} | null = null;
|
|
180
|
+
|
|
181
|
+
const rtlMultiplier = (): number => {
|
|
182
|
+
const handle = getHandle();
|
|
183
|
+
if (handle == null) {
|
|
184
|
+
return 1;
|
|
185
|
+
}
|
|
186
|
+
const view = handle.ownerDocument.defaultView;
|
|
187
|
+
return view?.getComputedStyle(handle).direction === 'rtl' ? -1 : 1;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const clearDrag = () => {
|
|
191
|
+
const ending = drag;
|
|
192
|
+
drag = null;
|
|
193
|
+
const target = ending?.target;
|
|
194
|
+
if (
|
|
195
|
+
ending != null &&
|
|
196
|
+
target != null &&
|
|
197
|
+
target.hasPointerCapture?.(ending.pointerId) === true
|
|
198
|
+
) {
|
|
199
|
+
target.releasePointerCapture(ending.pointerId);
|
|
200
|
+
}
|
|
201
|
+
if (ending != null) {
|
|
202
|
+
clearBodyDragStyles();
|
|
203
|
+
}
|
|
204
|
+
onDraggingChange?.(false);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
get isDragging() {
|
|
209
|
+
return drag != null;
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
pointerDown(event) {
|
|
213
|
+
const config = getConfig();
|
|
214
|
+
if (config.isDisabled || drag != null) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
event.preventDefault();
|
|
218
|
+
event.stopPropagation();
|
|
219
|
+
// An element from another document (an iframe, a popup) is not an
|
|
220
|
+
// instance of this page's Element, so test for the capture API itself.
|
|
221
|
+
const current = event.currentTarget as Element | null;
|
|
222
|
+
const target =
|
|
223
|
+
typeof current?.setPointerCapture === 'function' ? current : null;
|
|
224
|
+
target?.setPointerCapture?.(event.pointerId);
|
|
225
|
+
const isHorizontal = config.direction === 'horizontal';
|
|
226
|
+
drag = {
|
|
227
|
+
pointerId: event.pointerId,
|
|
228
|
+
startPos: isHorizontal ? event.clientX : event.clientY,
|
|
229
|
+
rtl: isHorizontal ? rtlMultiplier() : 1,
|
|
230
|
+
target,
|
|
231
|
+
};
|
|
232
|
+
onDraggingChange?.(true);
|
|
233
|
+
model.start();
|
|
234
|
+
applyBodyDragStyles(isHorizontal);
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
pointerMove(event) {
|
|
238
|
+
if (drag == null || drag.pointerId !== event.pointerId) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const config = getConfig();
|
|
242
|
+
const position =
|
|
243
|
+
config.direction === 'horizontal' ? event.clientX : event.clientY;
|
|
244
|
+
const sign = config.isReversed ? -1 : 1;
|
|
245
|
+
model.move((position - drag.startPos) * drag.rtl * sign);
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
pointerUp(event) {
|
|
249
|
+
if (drag?.pointerId !== event.pointerId) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
clearDrag();
|
|
253
|
+
model.end();
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
// Serves pointercancel and lostpointercapture alike: an interrupted drag
|
|
257
|
+
// ends without a resize end. The implicit release after every pointerup
|
|
258
|
+
// or pointercancel also fires lostpointercapture. By then the drag is
|
|
259
|
+
// already cleared, and this is a no-op.
|
|
260
|
+
pointerCancel(event) {
|
|
261
|
+
if (drag?.pointerId !== event.pointerId) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
clearDrag();
|
|
265
|
+
model.cancel();
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
interrupt() {
|
|
269
|
+
if (drag == null) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
clearDrag();
|
|
273
|
+
model.cancel();
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
keyDown(event) {
|
|
277
|
+
const config = getConfig();
|
|
278
|
+
if (config.isDisabled) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const state = model.state();
|
|
282
|
+
const isHorizontal = config.direction === 'horizontal';
|
|
283
|
+
const sign = config.isReversed ? -1 : 1;
|
|
284
|
+
const keyStep = event.shiftKey ? KEYBOARD_LARGE_STEP : KEYBOARD_STEP;
|
|
285
|
+
const rtl = isHorizontal ? rtlMultiplier() : 1;
|
|
286
|
+
|
|
287
|
+
// The pane's grow direction on this key: ArrowRight/Down grow unless the
|
|
288
|
+
// layout is reversed or, on the inline axis, right-to-left.
|
|
289
|
+
const grows = (key: string) =>
|
|
290
|
+
(key === 'ArrowRight' || key === 'ArrowDown'
|
|
291
|
+
? 1
|
|
292
|
+
: key === 'ArrowLeft' || key === 'ArrowUp'
|
|
293
|
+
? -1
|
|
294
|
+
: 0) *
|
|
295
|
+
(isHorizontal ? rtl : 1) *
|
|
296
|
+
sign >
|
|
297
|
+
0;
|
|
298
|
+
|
|
299
|
+
switch (event.key) {
|
|
300
|
+
case 'ArrowRight':
|
|
301
|
+
case 'ArrowDown':
|
|
302
|
+
case 'ArrowLeft':
|
|
303
|
+
case 'ArrowUp': {
|
|
304
|
+
event.preventDefault();
|
|
305
|
+
if (state.isCollapsed) {
|
|
306
|
+
// A collapsed pane answers only the grow arrow. It opens one step
|
|
307
|
+
// above its minimum, the value the separator announces while
|
|
308
|
+
// collapsed. If the collapse threshold is larger, it opens there.
|
|
309
|
+
// A bare step would stay below the threshold and leave the pane
|
|
310
|
+
// closed. The shrink arrow is a no-op.
|
|
311
|
+
if (grows(event.key)) {
|
|
312
|
+
step(model, Math.max(state.minPx + keyStep, state.collapsedSize));
|
|
313
|
+
}
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
if (state.snaps.length > 0) {
|
|
317
|
+
// With snap points the region rests only on a point, so a pixel
|
|
318
|
+
// step would snap straight back. The arrow moves to the adjacent
|
|
319
|
+
// point in its direction instead, two points with Shift, within
|
|
320
|
+
// the bounds.
|
|
321
|
+
step(
|
|
322
|
+
model,
|
|
323
|
+
adjacentSnapPoint(
|
|
324
|
+
state,
|
|
325
|
+
grows(event.key) ? 1 : -1,
|
|
326
|
+
event.shiftKey ? 2 : 1,
|
|
327
|
+
) - state.size,
|
|
328
|
+
);
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
const direction =
|
|
332
|
+
event.key === 'ArrowRight' || event.key === 'ArrowDown' ? 1 : -1;
|
|
333
|
+
step(model, direction * keyStep * (isHorizontal ? rtl : 1) * sign);
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
case 'Home': {
|
|
337
|
+
event.preventDefault();
|
|
338
|
+
step(model, state.minPx - state.size);
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
case 'End': {
|
|
342
|
+
event.preventDefault();
|
|
343
|
+
// Only a configured maximum is the region's own limit. The reported
|
|
344
|
+
// stand-in for an unbounded region is where the pane could reach,
|
|
345
|
+
// not where End may move it.
|
|
346
|
+
if (state.maxPx !== Infinity) {
|
|
347
|
+
step(model, state.maxPx - state.size);
|
|
348
|
+
}
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
case 'Enter': {
|
|
352
|
+
event.preventDefault();
|
|
353
|
+
if (state.collapsible) {
|
|
354
|
+
toggleCollapse(model);
|
|
355
|
+
}
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
doubleClick() {
|
|
362
|
+
if (getConfig().isDisabled || !model.state().collapsible) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
toggleCollapse(model);
|
|
366
|
+
},
|
|
367
|
+
};
|
|
368
|
+
}
|
|
@@ -72,7 +72,7 @@ import {
|
|
|
72
72
|
SideNavCollapseContext,
|
|
73
73
|
} from './SideNavCollapseContext';
|
|
74
74
|
import {useSideNavRenderMode} from './SideNavRenderContext';
|
|
75
|
-
import {
|
|
75
|
+
import {useCloseMobileDrawerOnActivate} from '../AppShell/useCloseMobileDrawerOnActivate';
|
|
76
76
|
import {themeProps} from '../utils/themeProps';
|
|
77
77
|
import {disabledInteractionProps} from '../utils/disabledInteraction';
|
|
78
78
|
|
|
@@ -535,8 +535,8 @@ export function SideNavItem({
|
|
|
535
535
|
const t = useComponentTranslator();
|
|
536
536
|
const {isCollapsed} = useSideNavCollapse();
|
|
537
537
|
const renderMode = useSideNavRenderMode();
|
|
538
|
-
const {closeMobileNav} = useAppShellMobile();
|
|
539
538
|
const isInDrawer = renderMode === 'drawer' || renderMode === 'drawer-content';
|
|
539
|
+
const closeDrawerAfter = useCloseMobileDrawerOnActivate(isInDrawer);
|
|
540
540
|
const id = useId();
|
|
541
541
|
const hasChildren = !!children;
|
|
542
542
|
const depth = use(SideNavItemDepthContext);
|
|
@@ -625,11 +625,7 @@ export function SideNavItem({
|
|
|
625
625
|
toggleItemCollapse();
|
|
626
626
|
return;
|
|
627
627
|
}
|
|
628
|
-
onClick?.(e);
|
|
629
|
-
// Close the mobile nav when a nav item is activated inside the drawer
|
|
630
|
-
if (isInDrawer) {
|
|
631
|
-
closeMobileNav();
|
|
632
|
-
}
|
|
628
|
+
closeDrawerAfter(onClick)?.(e);
|
|
633
629
|
};
|
|
634
630
|
|
|
635
631
|
const handleToggleClick = (e: React.MouseEvent) => {
|
package/src/Token/Token.tsx
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* - /packages/cli/assets/templates/blocks/components/Token/ (showcase blocks)
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import type
|
|
20
|
+
import {use, type ReactNode} from 'react';
|
|
21
21
|
import * as stylex from '@stylexjs/stylex';
|
|
22
22
|
import {
|
|
23
23
|
colorVars,
|
|
@@ -41,6 +41,7 @@ import {interactionOverlayStyles} from '../utils/interactionOverlay.stylex';
|
|
|
41
41
|
import {usePressFeedback} from '../hooks/usePressFeedback';
|
|
42
42
|
import type {TokenColorMap} from './index';
|
|
43
43
|
import {disabledInteractionProps} from '../utils/disabledInteraction';
|
|
44
|
+
import {TokenNamingContext} from './TokenNamingContext';
|
|
44
45
|
import {
|
|
45
46
|
registerEnglishFallback,
|
|
46
47
|
useComponentTranslator,
|
|
@@ -309,6 +310,8 @@ export function Token({
|
|
|
309
310
|
'data-testid': testId,
|
|
310
311
|
'aria-labelledby': ariaLabelledBy,
|
|
311
312
|
'aria-describedby': ariaDescribedBy,
|
|
313
|
+
'aria-haspopup': ariaHasPopup,
|
|
314
|
+
'aria-expanded': ariaExpanded,
|
|
312
315
|
ref,
|
|
313
316
|
...rest
|
|
314
317
|
}: TokenProps) {
|
|
@@ -319,6 +322,7 @@ export function Token({
|
|
|
319
322
|
const t = useComponentTranslator();
|
|
320
323
|
const LinkComponent = useLinkComponent();
|
|
321
324
|
const role = useInteractiveRole({href, onClick, isDisabled});
|
|
325
|
+
const naming = use(TokenNamingContext);
|
|
322
326
|
|
|
323
327
|
// When role is 'button' via context (no explicit onClick), treat as
|
|
324
328
|
// if onClick was provided — the popover attaches its own handler.
|
|
@@ -327,7 +331,9 @@ export function Token({
|
|
|
327
331
|
const removeButton = onRemove != null && (
|
|
328
332
|
<button
|
|
329
333
|
type="button"
|
|
330
|
-
aria-label={t('@astryx.token.remove', {
|
|
334
|
+
aria-label={t('@astryx.token.remove', {
|
|
335
|
+
label: naming?.removeName ?? label,
|
|
336
|
+
})}
|
|
331
337
|
onClick={e => {
|
|
332
338
|
e.stopPropagation();
|
|
333
339
|
onRemove(e);
|
|
@@ -352,12 +358,14 @@ export function Token({
|
|
|
352
358
|
</>
|
|
353
359
|
);
|
|
354
360
|
|
|
355
|
-
// Naming inputs reach the element that owns the token's action:
|
|
356
|
-
// button of a clickable token, otherwise the root
|
|
361
|
+
// Naming and popup inputs reach the element that owns the token's action:
|
|
362
|
+
// the inner button of a clickable token, otherwise the root
|
|
357
363
|
// (architecture:public-component-api INV5).
|
|
358
364
|
const labellingProps = {
|
|
359
365
|
'aria-labelledby': ariaLabelledBy,
|
|
360
366
|
'aria-describedby': ariaDescribedBy,
|
|
367
|
+
'aria-haspopup': ariaHasPopup,
|
|
368
|
+
'aria-expanded': ariaExpanded,
|
|
361
369
|
};
|
|
362
370
|
|
|
363
371
|
const sharedProps = {
|
|
@@ -532,6 +540,7 @@ export function Token({
|
|
|
532
540
|
<button
|
|
533
541
|
type="button"
|
|
534
542
|
onClick={effectiveOnClick}
|
|
543
|
+
aria-label={ariaLabelledBy == null ? naming?.actionName : undefined}
|
|
535
544
|
{...labellingProps}
|
|
536
545
|
aria-disabled={isDisabled || undefined}
|
|
537
546
|
{...(isDisabled ? disabledInteractionProps() : null)}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file TokenNamingContext.ts
|
|
7
|
+
* @input Naming parts from a caller whose token value renders outside its label
|
|
8
|
+
* @output Internal context: the names of a token's action and remove button
|
|
9
|
+
* @position Internal to Core; not exported from the Token entry point
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {createContext} from 'react';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Names for a Token whose visible text runs past its label. PowerSearch
|
|
16
|
+
* provides them: its tokens show the value as `endContent`.
|
|
17
|
+
*
|
|
18
|
+
* - `actionName` names a clickable token's inner button in place of `label`.
|
|
19
|
+
* - `removeName` names the remove button in place of `label`. The remove word
|
|
20
|
+
* still comes from the catalog, so each locale keeps its word order.
|
|
21
|
+
*/
|
|
22
|
+
export interface TokenNaming {
|
|
23
|
+
actionName?: string;
|
|
24
|
+
removeName?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const TokenNamingContext = createContext<TokenNaming | null>(null);
|
|
28
|
+
TokenNamingContext.displayName = 'TokenNamingContext';
|
|
@@ -292,6 +292,13 @@ export const docs = {
|
|
|
292
292
|
type: 'CSSProperties',
|
|
293
293
|
description: 'Inline styles for the visual control box.',
|
|
294
294
|
},
|
|
295
|
+
{
|
|
296
|
+
name: 'experimental',
|
|
297
|
+
type: "{presentation?: 'popover' | 'bottom-sheet' | 'adaptive'}",
|
|
298
|
+
stability: 'experimental',
|
|
299
|
+
description:
|
|
300
|
+
"Experimental options: anything in this object may change or be removed in a patch release, and omitting it keeps the stable behavior. presentation (default 'popover') chooses how the field presents its choices. popover is the inline combobox with its anchored suggestion menu. bottom-sheet replaces it with a trigger showing the selected tokens that opens one tall modal sheet: a search row on top, full-width checkbox rows that keep their place while the sheet is open, a Create row with hasCreate, and a footer pinned in every state with a confirmed Clear all and Save; the sheet edits a draft, so onChange fires once, when Save commits it, and a swipe, scrim or Escape close of a changed draft asks before discarding. adaptive uses the bottom sheet on compact touch screens and the popover otherwise, resolved the same way Selector resolves it. In the sheet presentation the trigger is the control (ref, id, aria-* and data-* reach it; with experimental passed, ref also accepts a Ref<HTMLInputElement | HTMLButtonElement> — an input-only ref still type-checks against that wider type, but with adaptive or bottom-sheet it receives the trigger button at runtime, so type the ref for both elements; TokenizerProps keeps its released ref type and gains only the optional experimental), and renderItem, renderToken, endContent, hasClear, hasEntriesOnFocus, menuWidth, tokenOverflowBehavior and hasAutoFocus have no surface to act on; the field warns once in development when they are passed. For example: <Tokenizer label=\"Tags\" searchSource={tagSource} value={tags} onChange={setTags} hasCreate experimental={{presentation: 'adaptive'}} />.",
|
|
301
|
+
},
|
|
295
302
|
],
|
|
296
303
|
theming: {
|
|
297
304
|
targets: [
|
|
@@ -631,6 +638,13 @@ export const docsZh = {
|
|
|
631
638
|
type: 'CSSProperties',
|
|
632
639
|
description: '可视控件框的内联样式。',
|
|
633
640
|
},
|
|
641
|
+
{
|
|
642
|
+
name: 'experimental',
|
|
643
|
+
type: "{presentation?: 'popover' | 'bottom-sheet' | 'adaptive'}",
|
|
644
|
+
stability: 'experimental',
|
|
645
|
+
description:
|
|
646
|
+
"实验性选项:此对象中的任何内容都可能在补丁版本中更改或移除;省略它即保持稳定行为。presentation(默认 'popover')决定字段如何呈现选项:popover 为内联组合框及其锚定建议菜单;bottom-sheet 改为一个显示已选标记的触发按钮,点按后打开一个高的模态底部面板——顶部为搜索行,下方为整行复选框行(面板打开期间位置保持不变),hasCreate 时有「创建」行,底部固定显示需确认的「全部清除」和「保存」;面板编辑的是草稿,onChange 仅在「保存」提交时触发一次,草稿有改动时滑动、点击遮罩或按 Escape 关闭会先询问是否放弃;adaptive 在紧凑触摸屏上使用底部面板,其他情况使用 popover,解析方式与 Selector 相同。在底部面板呈现中,触发按钮即为控件(ref、id、aria-* 和 data-* 都作用于它;传入 experimental 后,ref 也接受 Ref<HTMLInputElement | HTMLButtonElement>——仅为输入框类型的 ref 在这个更宽的类型下仍能通过类型检查,但在 adaptive 或 bottom-sheet 下运行时收到的是触发按钮,因此请为两种元素声明 ref 的类型;TokenizerProps 保留已发布的 ref 类型,仅新增可选的 experimental);renderItem、renderToken、endContent、hasClear、hasEntriesOnFocus、menuWidth、tokenOverflowBehavior 和 hasAutoFocus 没有作用对象,传入时开发模式下会警告一次。例如:<Tokenizer label=\"标签\" searchSource={tagSource} value={tags} onChange={setTags} hasCreate experimental={{presentation: 'adaptive'}} />。",
|
|
647
|
+
},
|
|
634
648
|
],
|
|
635
649
|
theming: {
|
|
636
650
|
targets: [
|
|
@@ -830,5 +844,7 @@ export const docsDense = {
|
|
|
830
844
|
'Icon at input start, before tokens. Icon name, SVG component, or ReactNode.',
|
|
831
845
|
endContent: 'Content at input row end. For buttons, counts, controls.',
|
|
832
846
|
handleRef: 'Imperative handle for focus() and blur() control.',
|
|
847
|
+
experimental:
|
|
848
|
+
"Experimental; may change in a patch. presentation: 'popover' (default, inline combobox) | 'bottom-sheet' (trigger + one searchable modal sheet of checkbox rows editing a draft that Save commits; onChange fires once, on Save) | 'adaptive' (sheet on compact touch, else popover).",
|
|
833
849
|
},
|
|
834
850
|
};
|