@astryxdesign/core 0.1.0 → 0.1.1
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 +66 -0
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.js +5 -1
- package/dist/ContextMenu/ContextMenu.js +2 -2
- package/dist/DropdownMenu/DropdownMenu.js +2 -2
- package/dist/DropdownMenu/{renderXDSDropdownItems.d.ts → renderDropdownItems.d.ts} +3 -3
- package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -0
- package/dist/DropdownMenu/{renderXDSDropdownItems.js → renderDropdownItems.js} +2 -2
- package/dist/Layout/Layout.d.ts +10 -1
- package/dist/Layout/Layout.d.ts.map +1 -1
- package/dist/Layout/Layout.js +5 -1
- package/dist/Outline/Outline.d.ts +3 -2
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +23 -4
- package/dist/Outline/useScrollSpy.d.ts +14 -1
- package/dist/Outline/useScrollSpy.d.ts.map +1 -1
- package/dist/Outline/useScrollSpy.js +161 -50
- package/dist/Resizable/useResizable.d.ts.map +1 -1
- package/dist/Resizable/useResizable.js +1 -5
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +1 -1
- package/dist/ToggleButton/ToggleButton.d.ts +10 -3
- package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButton.js +64 -18
- package/dist/theme/Theme.js +1 -1
- package/dist/theme/defineTheme.d.ts +1 -1
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +1 -1
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.js +1 -1
- package/dist/theme/tokens.d.ts +1 -1
- package/dist/theme/tokens.js +4 -4
- package/dist/theme/useTheme.d.ts +2 -2
- package/dist/utils/dateParser.d.ts.map +1 -1
- package/dist/utils/dateParser.js +15 -2
- package/package.json +2 -2
- package/src/Chat/ChatLayoutScrollButton.tsx +7 -1
- package/src/Collapsible/useCollapsible.doc.mjs +2 -2
- package/src/ContextMenu/ContextMenu.tsx +2 -2
- package/src/DateInput/DateInput.test.tsx +68 -20
- package/src/Divider/Divider.doc.mjs +1 -1
- package/src/DropdownMenu/DropdownMenu.tsx +2 -2
- package/src/DropdownMenu/{renderXDSDropdownItems.tsx → renderDropdownItems.tsx} +2 -2
- package/src/FormLayout/FormLayout.doc.mjs +3 -3
- package/src/Icon/Icon.doc.mjs +4 -4
- package/src/Item/Item.doc.mjs +2 -2
- package/src/Layout/Layout.doc.mjs +2 -1
- package/src/Layout/Layout.tsx +15 -1
- package/src/Layout/__tests__/childrenAsContent.test.tsx +59 -0
- package/src/Link/Link.doc.mjs +3 -3
- package/src/Link/LinkProvider.doc.mjs +3 -3
- package/src/Markdown/Markdown.doc.mjs +4 -4
- package/src/Outline/Outline.doc.mjs +1 -1
- package/src/Outline/Outline.test.tsx +76 -38
- package/src/Outline/Outline.tsx +23 -4
- package/src/Outline/useScrollSpy.ts +196 -63
- package/src/Resizable/Resizable.doc.mjs +2 -2
- package/src/Resizable/useResizable.ts +1 -7
- package/src/Selector/Selector.tsx +5 -6
- package/src/Table/Table.doc.mjs +3 -3
- package/src/ToggleButton/ToggleButton.doc.mjs +2 -2
- package/src/ToggleButton/ToggleButton.test.tsx +148 -6
- package/src/ToggleButton/ToggleButton.tsx +83 -20
- package/src/hooks/useEntryAnimation.doc.mjs +3 -3
- package/src/hooks/useMediaQuery.doc.mjs +2 -2
- package/src/hooks/useStreamingText.doc.mjs +3 -3
- package/src/theme/Theme.doc.mjs +2 -2
- package/src/theme/Theme.tsx +1 -1
- package/src/theme/defineTheme.ts +1 -1
- package/src/theme/index.ts +1 -1
- package/src/theme/syntax/defineSyntaxTheme.ts +1 -1
- package/src/theme/tokens.ts +4 -4
- package/src/theme/useTheme.ts +2 -2
- package/src/utils/dateParser.test.ts +26 -0
- package/src/utils/dateParser.ts +16 -2
- package/dist/DropdownMenu/renderXDSDropdownItems.d.ts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
# @xds/core
|
|
2
2
|
|
|
3
|
+
# 0.1.1
|
|
4
|
+
|
|
5
|
+
#### Breaking Changes
|
|
6
|
+
|
|
7
|
+
- Rename `xdsTokenDefaults` export to `tokenDefaults`
|
|
8
|
+
The token-defaults constant is renamed from `xdsTokenDefaults` to
|
|
9
|
+
`tokenDefaults` (exported from `@astryxdesign/core/theme`). Update imports
|
|
10
|
+
accordingly. Part of removing xds naming from the public API.
|
|
11
|
+
|
|
12
|
+
#### Fixes
|
|
13
|
+
|
|
14
|
+
- Increase trailing padding on `ChatLayoutScrollButton` when a label is shown
|
|
15
|
+
With a label (e.g. "New messages"), the chevron icon sits on the leading edge
|
|
16
|
+
and the text on the trailing edge. The symmetric inline padding left the label
|
|
17
|
+
text cramped against the pill's rounded corner. The trailing inline padding is
|
|
18
|
+
now widened when a label is present, giving the text comfortable breathing room
|
|
19
|
+
from the rounded edge. The icon-only (collapsed) state is unchanged and stays
|
|
20
|
+
balanced.
|
|
21
|
+
- Prevent `DateInput` from crashing the page while typing an incomplete
|
|
22
|
+
date. Typing a leading `0` or `1` (e.g. starting to enter `01` for January)
|
|
23
|
+
could coerce the in-progress value into an invalid date with a year of `0`,
|
|
24
|
+
which then threw a `RangeError` and crashed the surrounding page. Partial,
|
|
25
|
+
not-yet-complete input is now treated as incomplete instead of being parsed
|
|
26
|
+
into a date, so the field stays usable as you type.
|
|
27
|
+
- Remove doubled focus ring on `Selector`. The inner combobox button drew
|
|
28
|
+
its own `:focus-visible` outline on top of the wrapper's `:focus-within` ring,
|
|
29
|
+
producing a stacked, rounded outline over the trigger after selecting an option
|
|
30
|
+
or navigating with the keyboard. The button now defers to the wrapper's focus
|
|
31
|
+
ring, matching `TextInput` and `NumberInput`.
|
|
32
|
+
- `<Layout>…</Layout>` no longer renders a blank page. `Layout` is
|
|
33
|
+
slot-driven (`content`/`header`/`start`/`end`/`footer`), and the natural nested
|
|
34
|
+
form `<Layout><LayoutContent /></Layout>` previously type-checked and built
|
|
35
|
+
green while dropping its children at runtime — an empty shell. Children now
|
|
36
|
+
render as a shorthand for the `content` slot (`<Layout>{main}</Layout>` is
|
|
37
|
+
equivalent to `<Layout content={main} />`), matching how `Card` and `Section`
|
|
38
|
+
accept content; an explicit `content` prop still wins when both are provided.
|
|
39
|
+
- ToggleButton runs pressedChangeAction in an interruptible transition with optimistic state
|
|
40
|
+
`pressedChangeAction` was fired as a non-awaited promise, so the documented
|
|
41
|
+
loading spinner never appeared and the toggle ignored the action's lifecycle.
|
|
42
|
+
It now runs inside a transition with an optimistic pressed state, matching
|
|
43
|
+
`Switch`:
|
|
44
|
+
|
|
45
|
+
#### Other Changes
|
|
46
|
+
|
|
47
|
+
- The optimistic pressed state flips immediately on click; the spinner is
|
|
48
|
+
debounced so a fast action shows the new state without a spinner flash.
|
|
49
|
+
- The action is interruptible — clicking again while it is pending starts a
|
|
50
|
+
new transition with the next optimistic state (e.g. true -> false -> true),
|
|
51
|
+
instead of being dropped or guarded out.
|
|
52
|
+
- Synchronous handlers are supported too: a `pressedChangeAction` (or
|
|
53
|
+
`onPressedChange`) that synchronously triggers a suspending update, such as
|
|
54
|
+
a router navigation that suspends on data, also drives the pending state.
|
|
55
|
+
`pressedChangeAction` now accepts `void | Promise<void>`.
|
|
56
|
+
|
|
57
|
+
#### Contributors
|
|
58
|
+
|
|
59
|
+
Thanks to everyone who contributed to this release:
|
|
60
|
+
|
|
61
|
+
- @cixzhang
|
|
62
|
+
- @ejhammond
|
|
63
|
+
- @josephfarina
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
3
67
|
# 0.1.0
|
|
4
68
|
|
|
5
69
|
#### Breaking Changes
|
|
@@ -52,9 +116,11 @@
|
|
|
52
116
|
- import {neutralTheme} from '@astryxdesign/theme-neutral/built';
|
|
53
117
|
- <Theme theme={defaultTheme}>...</Theme>
|
|
54
118
|
- <Theme theme={neutralTheme}>...</Theme>
|
|
119
|
+
|
|
55
120
|
```
|
|
56
121
|
|
|
57
122
|
```
|
|
123
|
+
|
|
58
124
|
- Rename the npm package scope from `@xds/*` to `@astryxdesign/*`
|
|
59
125
|
All published packages move to the new `@astryxdesign` scope (e.g. `@xds/core` → `@astryxdesign/core`), along with the workspace lockfile, build/runtime scope-directory scans, and docsite slug derivation. Consumers must update their imports and dependency names. The internal ESLint plugin namespace (`@xds/*` rules) is intentionally untouched and tracked separately. Existing `@xds/*` codemods continue to target the old scope so projects still on `@xds/*` can migrate.
|
|
60
126
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatLayoutScrollButton.d.ts","sourceRoot":"","sources":["../../src/Chat/ChatLayoutScrollButton.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAY1B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAO5C,MAAM,WAAW,2BAA4B,SAAQ,IAAI,CACvD,SAAS,CAAC,cAAc,CAAC,EACzB,SAAS,CACV;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,qCAAqC;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"ChatLayoutScrollButton.d.ts","sourceRoot":"","sources":["../../src/Chat/ChatLayoutScrollButton.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAY1B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAO5C,MAAM,WAAW,2BAA4B,SAAQ,IAAI,CACvD,SAAS,CAAC,cAAc,CAAC,EACzB,SAAS,CACV;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,qCAAqC;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAwDD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,GAAG,EACH,SAAS,EACT,KAAK,EACL,OAAO,EACP,MAAM,EACN,SAAS,EACT,KAAK,GACN,EAAE,2BAA2B,qBAwB7B;yBAhCe,sBAAsB"}
|
|
@@ -43,6 +43,10 @@ const styles = {
|
|
|
43
43
|
khDVqt: "xuxw1ft",
|
|
44
44
|
kg3NbH: "xf314gf",
|
|
45
45
|
$$css: true
|
|
46
|
+
},
|
|
47
|
+
buttonWithLabel: {
|
|
48
|
+
kwRFfy: "x1t818jl",
|
|
49
|
+
$$css: true
|
|
46
50
|
}
|
|
47
51
|
};
|
|
48
52
|
|
|
@@ -95,7 +99,7 @@ export function ChatLayoutScrollButton({
|
|
|
95
99
|
variant: "ghost",
|
|
96
100
|
size: "md",
|
|
97
101
|
onClick: onClick,
|
|
98
|
-
xstyle: styles.button,
|
|
102
|
+
xstyle: [styles.button, label ? styles.buttonWithLabel : null],
|
|
99
103
|
children: label ?? undefined
|
|
100
104
|
})
|
|
101
105
|
})
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
import React, { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
28
28
|
import * as stylex from '@stylexjs/stylex';
|
|
29
29
|
import { useLayer } from "../Layer/useLayer.js";
|
|
30
|
-
import {
|
|
30
|
+
import { renderDropdownItems } from "../DropdownMenu/renderDropdownItems.js";
|
|
31
31
|
import { DropdownMenuContext } from "../DropdownMenu/DropdownMenuContext.js";
|
|
32
32
|
import { useListFocus } from "../hooks/useListFocus.js";
|
|
33
33
|
import { layerAnimations } from "../Layer/layerAnimations.stylex.js";
|
|
@@ -199,7 +199,7 @@ export function ContextMenu({
|
|
|
199
199
|
closeMenu,
|
|
200
200
|
menuSize: size
|
|
201
201
|
}), [closeMenu, size]);
|
|
202
|
-
const resolvedMenuContent = props.items !== undefined ?
|
|
202
|
+
const resolvedMenuContent = props.items !== undefined ? renderDropdownItems(items) : menuContent;
|
|
203
203
|
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
204
204
|
children: [/*#__PURE__*/_jsx("div", {
|
|
205
205
|
ref: ref,
|
|
@@ -26,7 +26,7 @@ import * as stylex from '@stylexjs/stylex';
|
|
|
26
26
|
import { usePopover } from "../Popover/usePopover.js";
|
|
27
27
|
import { Button } from "../Button/index.js";
|
|
28
28
|
import { Icon } from "../Icon/index.js";
|
|
29
|
-
import {
|
|
29
|
+
import { renderDropdownItems } from "./renderDropdownItems.js";
|
|
30
30
|
import { DropdownMenuContext } from "./DropdownMenuContext.js";
|
|
31
31
|
import { useListFocus } from "../hooks/useListFocus.js";
|
|
32
32
|
import { layerAnimations } from "../Layer/layerAnimations.stylex.js";
|
|
@@ -265,7 +265,7 @@ export function DropdownMenu({
|
|
|
265
265
|
}), [closeMenu, menuSize]);
|
|
266
266
|
|
|
267
267
|
// Resolve menu content: data-driven items become components
|
|
268
|
-
const menuContent = props.items !== undefined ?
|
|
268
|
+
const menuContent = props.items !== undefined ? renderDropdownItems(items) : children;
|
|
269
269
|
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
270
270
|
children: [/*#__PURE__*/_jsx(Button, {
|
|
271
271
|
...button,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file
|
|
2
|
+
* @file renderDropdownItems.tsx
|
|
3
3
|
* @output Converts data-driven menu items into DropdownMenuItem components
|
|
4
4
|
* @position Utility; used by DropdownMenu to unify data-driven and compound paths
|
|
5
5
|
*/
|
|
@@ -9,5 +9,5 @@ import type { DropdownMenuOption } from './DropdownMenu';
|
|
|
9
9
|
* Converts data-driven items into DropdownMenuItem components,
|
|
10
10
|
* so both modes share the same rendering and keyboard navigation path.
|
|
11
11
|
*/
|
|
12
|
-
export declare function
|
|
13
|
-
//# sourceMappingURL=
|
|
12
|
+
export declare function renderDropdownItems(items: DropdownMenuOption[]): ReactNode;
|
|
13
|
+
//# sourceMappingURL=renderDropdownItems.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderDropdownItems.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/renderDropdownItems.tsx"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAUrC,OAAO,KAAK,EAEV,kBAAkB,EAEnB,MAAM,gBAAgB,CAAC;AAyBxB;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,kBAAkB,EAAE,GAC1B,SAAS,CA8CX"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @file
|
|
4
|
+
* @file renderDropdownItems.tsx
|
|
5
5
|
* @output Converts data-driven menu items into DropdownMenuItem components
|
|
6
6
|
* @position Utility; used by DropdownMenu to unify data-driven and compound paths
|
|
7
7
|
*/
|
|
@@ -29,7 +29,7 @@ function getSectionKey(section, index) {
|
|
|
29
29
|
* Converts data-driven items into DropdownMenuItem components,
|
|
30
30
|
* so both modes share the same rendering and keyboard navigation path.
|
|
31
31
|
*/
|
|
32
|
-
export function
|
|
32
|
+
export function renderDropdownItems(items) {
|
|
33
33
|
const elements = [];
|
|
34
34
|
for (let i = 0; i < items.length; i++) {
|
|
35
35
|
const option = items[i];
|
package/dist/Layout/Layout.d.ts
CHANGED
|
@@ -83,6 +83,15 @@ export interface LayoutProps extends Omit<BaseProps, 'content'> {
|
|
|
83
83
|
* Inline styles to apply to the root element.
|
|
84
84
|
*/
|
|
85
85
|
style?: React.CSSProperties;
|
|
86
|
+
/**
|
|
87
|
+
* Children are a shorthand for the `content` slot:
|
|
88
|
+
* `<Layout>{main}</Layout>` is equivalent to `<Layout content={main} />`.
|
|
89
|
+
* The surrounding zones (`header`/`start`/`end`/`footer`) stay explicit
|
|
90
|
+
* props. If both `content` and `children` are provided, `content` wins.
|
|
91
|
+
* Accepting children keeps the natural `<Layout>…</Layout>` form from
|
|
92
|
+
* rendering a blank shell.
|
|
93
|
+
*/
|
|
94
|
+
children?: ReactNode;
|
|
86
95
|
}
|
|
87
96
|
/**
|
|
88
97
|
* Page shell with header, sidebar(s), content, and footer slots.
|
|
@@ -130,7 +139,7 @@ export interface LayoutProps extends Omit<BaseProps, 'content'> {
|
|
|
130
139
|
* />
|
|
131
140
|
* ```
|
|
132
141
|
*/
|
|
133
|
-
export declare function Layout({ content, contentWidth, defaultHasDividers, end, footer, header, height, padding, ref, start, xstyle, className, style, }: LayoutProps): import("react").JSX.Element;
|
|
142
|
+
export declare function Layout({ children, content, contentWidth, defaultHasDividers, end, footer, header, height, padding, ref, start, xstyle, className, style, }: LayoutProps): import("react").JSX.Element;
|
|
134
143
|
export declare namespace Layout {
|
|
135
144
|
var displayName: string;
|
|
136
145
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Layout.d.ts","sourceRoot":"","sources":["../../src/Layout/Layout.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAC,KAAK,SAAS,EAAU,MAAM,OAAO,CAAC;AAQ9C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAOhD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAgD3C,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC;IAC7D;;OAEG;IACH,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEhC;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,GAAG,CAAC,EAAE,SAAS,CAAC;IAEhB;;OAEG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB;;OAEG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IAEtB;;;;OAIG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IAEtB;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"Layout.d.ts","sourceRoot":"","sources":["../../src/Layout/Layout.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAC,KAAK,SAAS,EAAU,MAAM,OAAO,CAAC;AAQ9C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAOhD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAgD3C,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC;IAC7D;;OAEG;IACH,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEhC;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,GAAG,CAAC,EAAE,SAAS,CAAC;IAEhB;;OAEG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB;;OAEG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IAEtB;;;;OAIG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IAEtB;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAE5B;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,MAAM,CAAC,EACrB,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,kBAAkB,EAClB,GAAG,EACH,MAAM,EACN,MAAM,EACN,MAAe,EACf,OAAO,EACP,GAAG,EACH,KAAK,EACL,MAAM,EACN,SAAS,EACT,KAAK,GACN,EAAE,WAAW,+BA0Eb;yBAzFe,MAAM"}
|
package/dist/Layout/Layout.js
CHANGED
|
@@ -155,6 +155,7 @@ const _temp3 = {
|
|
|
155
155
|
$$css: true
|
|
156
156
|
};
|
|
157
157
|
export function Layout({
|
|
158
|
+
children,
|
|
158
159
|
content,
|
|
159
160
|
contentWidth,
|
|
160
161
|
defaultHasDividers,
|
|
@@ -170,6 +171,9 @@ export function Layout({
|
|
|
170
171
|
style
|
|
171
172
|
}) {
|
|
172
173
|
const isFill = height === 'fill';
|
|
174
|
+
// Children are a shorthand for the content slot; an explicit `content` prop
|
|
175
|
+
// wins when both are provided.
|
|
176
|
+
const resolvedContent = content ?? children;
|
|
173
177
|
const dividerCtxValue = useMemo(() => defaultHasDividers != null ? {
|
|
174
178
|
defaultHasDividers
|
|
175
179
|
} : null, [defaultHasDividers]);
|
|
@@ -212,7 +216,7 @@ export function Layout({
|
|
|
212
216
|
})),
|
|
213
217
|
children: /*#__PURE__*/_jsx(AreaProvider, {
|
|
214
218
|
area: "content",
|
|
215
|
-
children:
|
|
219
|
+
children: resolvedContent
|
|
216
220
|
})
|
|
217
221
|
}), /*#__PURE__*/_jsx(AreaProvider, {
|
|
218
222
|
area: "end",
|
|
@@ -29,8 +29,9 @@ export interface OutlineProps extends BaseProps<HTMLElement> {
|
|
|
29
29
|
* indentation based on each heading level. Features a sliding indicator
|
|
30
30
|
* track that animates to the active item.
|
|
31
31
|
*
|
|
32
|
-
* When `activeId` is omitted, it
|
|
33
|
-
*
|
|
32
|
+
* When `activeId` is omitted, it tracks scroll position and marks the last
|
|
33
|
+
* heading whose top has passed its activation line (its scroll-margin-top)
|
|
34
|
+
* active — defaulting to the first item at the top and the last at the bottom.
|
|
34
35
|
*
|
|
35
36
|
* @example
|
|
36
37
|
* ```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Outline.d.ts","sourceRoot":"","sources":["../../src/Outline/Outline.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAE5C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,SAAS,CAAC;AAGzC,YAAY,EAAC,WAAW,EAAC,MAAM,SAAS,CAAC;AAEzC,MAAM,WAAW,YAAa,SAAQ,SAAS,CAAC,WAAW,CAAC;IAC1D,6CAA6C;IAC7C,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAE7B,+CAA+C;IAC/C,KAAK,EAAE,WAAW,EAAE,CAAC;IAErB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAExC,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;;OAKG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAEhC,sCAAsC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAgJD
|
|
1
|
+
{"version":3,"file":"Outline.d.ts","sourceRoot":"","sources":["../../src/Outline/Outline.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAE5C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,SAAS,CAAC;AAGzC,YAAY,EAAC,WAAW,EAAC,MAAM,SAAS,CAAC;AAEzC,MAAM,WAAW,YAAa,SAAQ,SAAS,CAAC,WAAW,CAAC;IAC1D,6CAA6C;IAC7C,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAE7B,+CAA+C;IAC/C,KAAK,EAAE,WAAW,EAAE,CAAC;IAErB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAExC,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;;OAKG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAEhC,sCAAsC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAgJD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,QAAQ,EACR,gBAAgB,EAChB,KAA2B,EAC3B,OAAmB,EACnB,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,aAAa,EAAE,MAAM,EACrB,GAAG,KAAK,EACT,EAAE,YAAY,+BAyGd;yBArHe,OAAO"}
|
package/dist/Outline/Outline.js
CHANGED
|
@@ -133,8 +133,9 @@ function getIndentStyle(level) {
|
|
|
133
133
|
* indentation based on each heading level. Features a sliding indicator
|
|
134
134
|
* track that animates to the active item.
|
|
135
135
|
*
|
|
136
|
-
* When `activeId` is omitted, it
|
|
137
|
-
*
|
|
136
|
+
* When `activeId` is omitted, it tracks scroll position and marks the last
|
|
137
|
+
* heading whose top has passed its activation line (its scroll-margin-top)
|
|
138
|
+
* active — defaulting to the first item at the top and the last at the bottom.
|
|
138
139
|
*
|
|
139
140
|
* @example
|
|
140
141
|
* ```
|
|
@@ -162,7 +163,12 @@ export function Outline({
|
|
|
162
163
|
}) {
|
|
163
164
|
const rootRef = useRef(null);
|
|
164
165
|
const LinkComponent = useLinkComponent();
|
|
165
|
-
const
|
|
166
|
+
const isControlled = activeId !== undefined;
|
|
167
|
+
const {
|
|
168
|
+
activeId: resolvedActiveId,
|
|
169
|
+
setActiveId,
|
|
170
|
+
lockActiveId
|
|
171
|
+
} = useScrollSpy({
|
|
166
172
|
activeId,
|
|
167
173
|
items,
|
|
168
174
|
onActiveIdChange,
|
|
@@ -170,12 +176,25 @@ export function Outline({
|
|
|
170
176
|
});
|
|
171
177
|
const handleClick = id => event => {
|
|
172
178
|
const target = document.getElementById(id);
|
|
173
|
-
|
|
179
|
+
|
|
180
|
+
// Let the browser handle modified clicks (open in new tab, etc.) and
|
|
181
|
+
// missing targets without touching the active state.
|
|
174
182
|
if (target == null || event.defaultPrevented || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) {
|
|
175
183
|
return;
|
|
176
184
|
}
|
|
177
185
|
event.preventDefault();
|
|
178
186
|
window.history.pushState(null, '', `#${id}`);
|
|
187
|
+
|
|
188
|
+
// Move the indicator to the clicked item in a single step. Controlled
|
|
189
|
+
// consumers own the active state (notify only); uncontrolled mode pins
|
|
190
|
+
// the active id and suppresses scroll-spy until the next manual scroll,
|
|
191
|
+
// so the click is honored — even for short/last sections — and the
|
|
192
|
+
// indicator doesn't chase the smooth scroll through other sections.
|
|
193
|
+
if (isControlled) {
|
|
194
|
+
setActiveId(id);
|
|
195
|
+
} else {
|
|
196
|
+
lockActiveId(id);
|
|
197
|
+
}
|
|
179
198
|
target.scrollIntoView({
|
|
180
199
|
behavior: 'smooth',
|
|
181
200
|
block: 'start'
|
|
@@ -5,6 +5,19 @@ interface UseScrollSpyOptions {
|
|
|
5
5
|
onActiveIdChange?: (id: string) => void;
|
|
6
6
|
rootRef: React.RefObject<HTMLElement | null>;
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
interface UseScrollSpyResult {
|
|
9
|
+
activeId: string | undefined;
|
|
10
|
+
/** Set the active id (notifies onActiveIdChange). For controlled consumers. */
|
|
11
|
+
setActiveId: (id: string) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Handle a click on the outline item with id `id`. Delays moving the
|
|
14
|
+
* indicator: scroll-spy is suppressed during the programmatic smooth scroll
|
|
15
|
+
* so the indicator doesn't chase it, then the indicator moves once to the
|
|
16
|
+
* clicked item when the scroll settles. If the user scrolls manually mid-way,
|
|
17
|
+
* scroll-position tracking resumes immediately instead.
|
|
18
|
+
*/
|
|
19
|
+
lockActiveId: (id: string) => void;
|
|
20
|
+
}
|
|
21
|
+
export declare function useScrollSpy({ activeId, items, onActiveIdChange, rootRef, }: UseScrollSpyOptions): UseScrollSpyResult;
|
|
9
22
|
export {};
|
|
10
23
|
//# sourceMappingURL=useScrollSpy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScrollSpy.d.ts","sourceRoot":"","sources":["../../src/Outline/useScrollSpy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useScrollSpy.d.ts","sourceRoot":"","sources":["../../src/Outline/useScrollSpy.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,SAAS,CAAC;AAuFzC,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC9C;AAED,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,+EAA+E;IAC/E,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;;;;;OAMG;IACH,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,KAAK,EACL,gBAAgB,EAChB,OAAO,GACR,EAAE,mBAAmB,GAAG,kBAAkB,CA2I1C"}
|
|
@@ -4,13 +4,23 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file useScrollSpy.ts
|
|
7
|
-
* @input Uses React,
|
|
7
|
+
* @input Uses React, scroll position of heading elements, OutlineItem type
|
|
8
8
|
* @output Exports internal useScrollSpy hook
|
|
9
9
|
* @position Internal behavior hook; consumed by Outline.tsx
|
|
10
10
|
*
|
|
11
|
+
* Drives the active outline item from scroll position. On each scroll
|
|
12
|
+
* (rAF-throttled) it reads live heading positions and marks the last heading
|
|
13
|
+
* whose top has passed its activation line (its own scroll-margin-top, i.e.
|
|
14
|
+
* where it lands when navigated to). This is stable — it never compares stale
|
|
15
|
+
* cached positions — so the indicator moves monotonically instead of jumping.
|
|
16
|
+
* Defaults to the first item at the top and the last item at the bottom so
|
|
17
|
+
* short final sections still activate.
|
|
18
|
+
*
|
|
11
19
|
* SYNC: When modified, update /packages/core/src/Outline/Outline.tsx
|
|
12
20
|
*/
|
|
13
|
-
import { useEffect, useRef, useState } from 'react';
|
|
21
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
22
|
+
/** Keys that scroll the viewport — used to detect a manual scroll intent. */
|
|
23
|
+
const SCROLL_KEYS = new Set(['ArrowUp', 'ArrowDown', 'PageUp', 'PageDown', 'Home', 'End', ' ', 'Spacebar']);
|
|
14
24
|
function getScrollableAncestor(element) {
|
|
15
25
|
let current = element?.parentElement ?? null;
|
|
16
26
|
while (current != null) {
|
|
@@ -24,6 +34,41 @@ function getScrollableAncestor(element) {
|
|
|
24
34
|
}
|
|
25
35
|
return null;
|
|
26
36
|
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the active heading id from current scroll position.
|
|
40
|
+
*
|
|
41
|
+
* A heading is "passed" once its top reaches its activation line — the scroll
|
|
42
|
+
* root's top plus the heading's own scroll-margin-top. The active heading is
|
|
43
|
+
* the last passed one (headings are in document order). When none have passed
|
|
44
|
+
* (scrolled above the first), the first item is active; at the bottom, the
|
|
45
|
+
* last item is active.
|
|
46
|
+
*/
|
|
47
|
+
function resolveActiveId(items, scrollRoot) {
|
|
48
|
+
if (items.length === 0) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
const rootTop = scrollRoot != null ? scrollRoot.getBoundingClientRect().top : 0;
|
|
52
|
+
const atBottom = scrollRoot != null ? scrollRoot.scrollTop + scrollRoot.clientHeight >= scrollRoot.scrollHeight - 2 : window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 2;
|
|
53
|
+
if (atBottom) {
|
|
54
|
+
return items[items.length - 1].id;
|
|
55
|
+
}
|
|
56
|
+
let activeId = items[0].id;
|
|
57
|
+
for (const item of items) {
|
|
58
|
+
const element = document.getElementById(item.id);
|
|
59
|
+
if (element == null) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const top = element.getBoundingClientRect().top;
|
|
63
|
+
const marginTop = Number.parseFloat(window.getComputedStyle(element).scrollMarginTop) || 0;
|
|
64
|
+
if (top <= rootTop + marginTop + 1) {
|
|
65
|
+
activeId = item.id;
|
|
66
|
+
} else {
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return activeId;
|
|
71
|
+
}
|
|
27
72
|
export function useScrollSpy({
|
|
28
73
|
activeId,
|
|
29
74
|
items,
|
|
@@ -32,72 +77,138 @@ export function useScrollSpy({
|
|
|
32
77
|
}) {
|
|
33
78
|
const isControlled = activeId !== undefined;
|
|
34
79
|
const [uncontrolledActiveId, setUncontrolledActiveId] = useState(items[0]?.id);
|
|
35
|
-
const visibleHeadingIdsRef = useRef(new Set());
|
|
36
|
-
const headingTopRef = useRef(new Map());
|
|
37
80
|
const activeIdRef = useRef(activeId);
|
|
81
|
+
// While true, scroll-spy ignores scroll updates because a click is driving a
|
|
82
|
+
// programmatic scroll. Released when that scroll settles or the user scrolls.
|
|
83
|
+
const suppressRef = useRef(false);
|
|
84
|
+
const releaseSuppressionRef = useRef(null);
|
|
85
|
+
// Latest scroll-position resolver, so the click handler can resume tracking
|
|
86
|
+
// when the user scrolls during a programmatic scroll.
|
|
87
|
+
const syncRef = useRef(null);
|
|
88
|
+
// Keep latest items/callback in refs so the scroll listener effect doesn't
|
|
89
|
+
// re-subscribe on every render (items is a fresh array each render).
|
|
90
|
+
const itemsRef = useRef(items);
|
|
91
|
+
itemsRef.current = items;
|
|
92
|
+
const onActiveIdChangeRef = useRef(onActiveIdChange);
|
|
93
|
+
onActiveIdChangeRef.current = onActiveIdChange;
|
|
38
94
|
const itemIds = items.map(item => item.id).join('\n');
|
|
39
95
|
activeIdRef.current = isControlled ? activeId : uncontrolledActiveId;
|
|
40
96
|
useEffect(() => {
|
|
41
|
-
if (isControlled || typeof
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const headingElements = items.map(item => document.getElementById(item.id)).filter(element => element != null);
|
|
45
|
-
if (headingElements.length === 0) {
|
|
97
|
+
if (isControlled || typeof window === 'undefined') {
|
|
46
98
|
return;
|
|
47
99
|
}
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
100
|
+
const scrollRoot = getScrollableAncestor(rootRef.current);
|
|
101
|
+
const scrollTarget = scrollRoot ?? window;
|
|
102
|
+
let frame = 0;
|
|
103
|
+
const update = () => {
|
|
104
|
+
frame = 0;
|
|
105
|
+
if (suppressRef.current) {
|
|
52
106
|
return;
|
|
53
107
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
let nextActiveId;
|
|
60
|
-
let nextTop = Number.POSITIVE_INFINITY;
|
|
61
|
-
for (const id of visibleHeadingIds) {
|
|
62
|
-
const top = headingTop.get(id) ?? Number.POSITIVE_INFINITY;
|
|
63
|
-
if (top < nextTop) {
|
|
64
|
-
nextTop = top;
|
|
65
|
-
nextActiveId = id;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (nextActiveId != null) {
|
|
69
|
-
setNextActiveId(nextActiveId);
|
|
108
|
+
const nextActiveId = resolveActiveId(itemsRef.current, scrollRoot);
|
|
109
|
+
if (nextActiveId != null && nextActiveId !== activeIdRef.current) {
|
|
110
|
+
activeIdRef.current = nextActiveId;
|
|
111
|
+
setUncontrolledActiveId(nextActiveId);
|
|
112
|
+
onActiveIdChangeRef.current?.(nextActiveId);
|
|
70
113
|
}
|
|
71
114
|
};
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
headingTop.set(id, entry.boundingClientRect.top);
|
|
76
|
-
if (entry.isIntersecting) {
|
|
77
|
-
visibleHeadingIds.add(id);
|
|
78
|
-
} else {
|
|
79
|
-
visibleHeadingIds.delete(id);
|
|
80
|
-
}
|
|
115
|
+
const onScroll = () => {
|
|
116
|
+
if (frame === 0) {
|
|
117
|
+
frame = requestAnimationFrame(update);
|
|
81
118
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
119
|
+
};
|
|
120
|
+
syncRef.current = update;
|
|
121
|
+
update();
|
|
122
|
+
scrollTarget.addEventListener('scroll', onScroll, {
|
|
123
|
+
passive: true
|
|
124
|
+
});
|
|
125
|
+
window.addEventListener('resize', onScroll, {
|
|
126
|
+
passive: true
|
|
86
127
|
});
|
|
87
|
-
for (const headingElement of headingElements) {
|
|
88
|
-
observer.observe(headingElement);
|
|
89
|
-
}
|
|
90
128
|
return () => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
129
|
+
syncRef.current = null;
|
|
130
|
+
scrollTarget.removeEventListener('scroll', onScroll);
|
|
131
|
+
window.removeEventListener('resize', onScroll);
|
|
132
|
+
if (frame !== 0) {
|
|
133
|
+
cancelAnimationFrame(frame);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}, [isControlled, itemIds, rootRef]);
|
|
137
|
+
|
|
138
|
+
// Tear down any pending suppression listeners when the Outline unmounts.
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
return () => {
|
|
141
|
+
releaseSuppressionRef.current?.();
|
|
94
142
|
};
|
|
95
|
-
}, [
|
|
143
|
+
}, []);
|
|
96
144
|
const setActiveId = nextActiveId => {
|
|
97
145
|
if (!isControlled) {
|
|
98
146
|
setUncontrolledActiveId(nextActiveId);
|
|
99
147
|
}
|
|
100
148
|
onActiveIdChange?.(nextActiveId);
|
|
101
149
|
};
|
|
102
|
-
|
|
150
|
+
const lockActiveId = useCallback(clickedId => {
|
|
151
|
+
if (typeof window === 'undefined') {
|
|
152
|
+
setUncontrolledActiveId(clickedId);
|
|
153
|
+
activeIdRef.current = clickedId;
|
|
154
|
+
onActiveIdChangeRef.current?.(clickedId);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Freeze the indicator during the programmatic smooth scroll instead of
|
|
159
|
+
// moving it immediately — it lands on the clicked item once the scroll
|
|
160
|
+
// settles, so it doesn't chase the scroll through intervening sections.
|
|
161
|
+
suppressRef.current = true;
|
|
162
|
+
// Replace any in-flight handlers from a previous click.
|
|
163
|
+
releaseSuppressionRef.current?.();
|
|
164
|
+
let settleTimer = 0;
|
|
165
|
+
const cleanup = () => {
|
|
166
|
+
window.removeEventListener('scrollend', onSettle);
|
|
167
|
+
window.removeEventListener('wheel', onManual);
|
|
168
|
+
window.removeEventListener('touchmove', onManual);
|
|
169
|
+
window.removeEventListener('keydown', onKeyDown);
|
|
170
|
+
if (settleTimer !== 0) {
|
|
171
|
+
clearTimeout(settleTimer);
|
|
172
|
+
settleTimer = 0;
|
|
173
|
+
}
|
|
174
|
+
releaseSuppressionRef.current = null;
|
|
175
|
+
};
|
|
176
|
+
// Programmatic scroll finished: move the indicator to the clicked item.
|
|
177
|
+
const onSettle = () => {
|
|
178
|
+
cleanup();
|
|
179
|
+
suppressRef.current = false;
|
|
180
|
+
setUncontrolledActiveId(clickedId);
|
|
181
|
+
activeIdRef.current = clickedId;
|
|
182
|
+
onActiveIdChangeRef.current?.(clickedId);
|
|
183
|
+
};
|
|
184
|
+
// User scrolled mid-flight: hand control back to scroll-position tracking.
|
|
185
|
+
const onManual = () => {
|
|
186
|
+
cleanup();
|
|
187
|
+
suppressRef.current = false;
|
|
188
|
+
syncRef.current?.();
|
|
189
|
+
};
|
|
190
|
+
const onKeyDown = event => {
|
|
191
|
+
if (SCROLL_KEYS.has(event.key)) {
|
|
192
|
+
onManual();
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
window.addEventListener('scrollend', onSettle, {
|
|
196
|
+
once: true
|
|
197
|
+
});
|
|
198
|
+
window.addEventListener('wheel', onManual, {
|
|
199
|
+
passive: true
|
|
200
|
+
});
|
|
201
|
+
window.addEventListener('touchmove', onManual, {
|
|
202
|
+
passive: true
|
|
203
|
+
});
|
|
204
|
+
window.addEventListener('keydown', onKeyDown);
|
|
205
|
+
// Fallback when scrollend is unsupported or no scroll is needed.
|
|
206
|
+
settleTimer = window.setTimeout(onSettle, 1200);
|
|
207
|
+
releaseSuppressionRef.current = cleanup;
|
|
208
|
+
}, []);
|
|
209
|
+
return {
|
|
210
|
+
activeId: isControlled ? activeId : uncontrolledActiveId,
|
|
211
|
+
setActiveId,
|
|
212
|
+
lockActiveId
|
|
213
|
+
};
|
|
103
214
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useResizable.d.ts","sourceRoot":"","sources":["../../src/Resizable/useResizable.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,qBAAqB;IACpC,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,wBAAyB,SAAQ,qBAAqB;IACrE,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,uBAAuB;IACtC,8CAA8C;IAC9C,SAAS,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACtC,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IAC/C,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,WAAW,EAAE,OAAO,CAAC;IACrB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,mCAAmC;IACnC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,wCAAwC;IACxC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,uEAAuE;IACvE,KAAK,EAAE,cAAc,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IAEd,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAE9C,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,IAAI,CAAC;CACzB;
|
|
1
|
+
{"version":3,"file":"useResizable.d.ts","sourceRoot":"","sources":["../../src/Resizable/useResizable.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,qBAAqB;IACpC,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,wBAAyB,SAAQ,qBAAqB;IACrE,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,uBAAuB;IACtC,8CAA8C;IAC9C,SAAS,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACtC,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IAC/C,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,WAAW,EAAE,OAAO,CAAC;IACrB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,mCAAmC;IACnC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,wCAAwC;IACxC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,uEAAuE;IACvE,KAAK,EAAE,cAAc,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IAEd,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAE9C,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,IAAI,CAAC;CACzB;AAyQD,wBAAgB,YAAY,CAAC,MAAM,EAAE,wBAAwB,GAAG,eAAe,CAAC;AAChF,wBAAgB,YAAY,CAC1B,MAAM,EAAE,uBAAuB,GAC9B,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC"}
|
|
@@ -27,10 +27,6 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
27
27
|
const DEFAULT_MIN = 50;
|
|
28
28
|
const DEFAULT_COLLAPSED_SIZE = 40;
|
|
29
29
|
const STORAGE_PREFIX = 'astryx-resizable:';
|
|
30
|
-
// Legacy key prefix read during the compat window so persisted panel sizes
|
|
31
|
-
// survive the xds -> astryx rename. Read-only fallback; we always write the
|
|
32
|
-
// new prefix. Removed at final cutover.
|
|
33
|
-
const LEGACY_STORAGE_PREFIX = 'xds-resizable:';
|
|
34
30
|
|
|
35
31
|
// =============================================================================
|
|
36
32
|
// Helpers
|
|
@@ -60,7 +56,7 @@ function loadPersistedSize(key) {
|
|
|
60
56
|
return null;
|
|
61
57
|
}
|
|
62
58
|
try {
|
|
63
|
-
const raw = localStorage.getItem(STORAGE_PREFIX + key)
|
|
59
|
+
const raw = localStorage.getItem(STORAGE_PREFIX + key);
|
|
64
60
|
if (raw != null) {
|
|
65
61
|
const parsed = JSON.parse(raw);
|
|
66
62
|
if (typeof parsed === 'number') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Selector.d.ts","sourceRoot":"","sources":["../../src/Selector/Selector.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,EASZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,EAAuB,KAAK,QAAQ,EAAC,MAAM,SAAS,CAAC;AAU5D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,mBAAmB,CAAC;AActD,OAAO,KAAK,EAAC,kBAAkB,EAAE,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAYpE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"Selector.d.ts","sourceRoot":"","sources":["../../src/Selector/Selector.tsx"],"names":[],"mappings":"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"}
|