@adecore/ui 0.38.0 → 0.40.0
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/dist/AccentSwatches.js +2 -3
- package/dist/Banner.js +1 -6
- package/dist/ChoiceCards.js +0 -5
- package/dist/ColorSwatch.js +1 -4
- package/dist/ColorWheelPicker.js +3 -6
- package/dist/DockShell.js +3 -5
- package/dist/EmptyState.js +0 -1
- package/dist/ErrorBoundary.js +1 -5
- package/dist/Field.js +2 -6
- package/dist/FileIcon.js +2 -4
- package/dist/Icon.js +1 -2
- package/dist/IconButton.js +1 -1
- package/dist/IconPicker.js +3 -7
- package/dist/Input.js +1 -2
- package/dist/Kbd.js +1 -3
- package/dist/LazyBoundary.js +2 -3
- package/dist/Meter.js +0 -1
- package/dist/MiddleTruncate.js +2 -6
- package/dist/PanelEmpty.js +0 -1
- package/dist/PanelHeader.js +1 -4
- package/dist/Pill.js +1 -6
- package/dist/ProjectSwitcher.js +1 -1
- package/dist/PromptDialog.js +5 -8
- package/dist/RelativeTime.js +1 -1
- package/dist/SegmentBar.js +1 -2
- package/dist/Segmented.js +2 -5
- package/dist/Select.js +1 -4
- package/dist/ShortcutHints.js +2 -3
- package/dist/Slider.js +0 -1
- package/dist/SlidingColumn.js +2 -4
- package/dist/SplitButton.js +1 -5
- package/dist/TextMenu.js +1 -5
- package/dist/Tile.js +0 -4
- package/dist/Toasts.js +1 -9
- package/dist/Tooltip.js +1 -1
- package/dist/UIProvider.js +0 -5
- package/dist/Waveform.js +4 -2
- package/dist/Wipe.js +1 -6
- package/dist/ZoomControls.js +0 -1
- package/dist/clipboard.js +2 -5
- package/dist/combobox/parts.js +3 -9
- package/dist/command-palette/parts.js +5 -14
- package/dist/dialog/parts.js +5 -10
- package/dist/dialog-layer.js +2 -3
- package/dist/error-boundary.js +4 -20
- package/dist/file-icon.js +4 -9
- package/dist/file-tree/empty-lines.js +2 -8
- package/dist/file-tree/use-empty-lines.js +1 -5
- package/dist/file-tree/use-slots.js +6 -10
- package/dist/format/datetime.js +32 -29
- package/dist/format/duration.js +2 -10
- package/dist/format/locale.js +19 -39
- package/dist/format/number.js +9 -22
- package/dist/format/regions.js +4 -7
- package/dist/icon-picker.js +5 -14
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/key-value-list/parts.js +1 -5
- package/dist/lazy.js +4 -19
- package/dist/locales.js +2 -6
- package/dist/menu/parts.js +61 -133
- package/dist/middle-truncate.js +2 -3
- package/dist/placement.js +0 -1
- package/dist/popover/parts.js +1 -5
- package/dist/popup-layer.js +2 -4
- package/dist/prefetch.js +2 -5
- package/dist/selection.js +1 -5
- package/dist/settings/MasterDetail.js +1 -7
- package/dist/settings/SettingsDialog.js +1 -2
- package/dist/settings/SettingsRow.js +2 -5
- package/dist/settings/SettingsSection.js +1 -1
- package/dist/shortcut-hints.js +1 -5
- package/dist/shortcut.js +1 -2
- package/dist/tabs/overflow.js +1 -6
- package/dist/tabs/parts.js +69 -95
- package/dist/testing/dedupe.js +5 -7
- package/dist/tint.d.ts +2 -0
- package/dist/tint.js +11 -0
- package/dist/toast-store.js +2 -10
- package/dist/tree/use-tree-shift.js +2 -6
- package/dist/useColumnResize.js +9 -10
- package/dist/useNow.js +2 -9
- package/dist/workspace/DropPreview.js +1 -5
- package/dist/workspace/preview.js +4 -9
- package/package.json +9 -9
- package/src/AccentSwatches.tsx +9 -3
- package/src/Banner.tsx +5 -11
- package/src/Button.tsx +4 -0
- package/src/Checkbox.tsx +3 -0
- package/src/ChoiceCards.tsx +12 -11
- package/src/CloseButton.tsx +1 -0
- package/src/ColorSwatch.tsx +7 -8
- package/src/ColorWheelPicker.tsx +28 -12
- package/src/ColumnResizeHandle.tsx +1 -0
- package/src/CopyButton.tsx +6 -0
- package/src/DisabledReason.tsx +1 -0
- package/src/DockShell.tsx +11 -10
- package/src/EmptyState.tsx +10 -6
- package/src/ErrorBoundary.tsx +13 -10
- package/src/Field.tsx +11 -13
- package/src/FileIcon.tsx +4 -4
- package/src/Icon.tsx +2 -4
- package/src/IconButton.tsx +14 -5
- package/src/IconPicker.tsx +30 -15
- package/src/Input.tsx +11 -9
- package/src/Kbd.tsx +8 -7
- package/src/LazyBoundary.tsx +3 -3
- package/src/Meter.tsx +7 -2
- package/src/MiddleTruncate.tsx +15 -6
- package/src/PanelEmpty.tsx +9 -7
- package/src/PanelHeader.tsx +2 -5
- package/src/Pill.tsx +7 -8
- package/src/ProjectSwitcher.tsx +16 -6
- package/src/PromptDialog.tsx +31 -26
- package/src/RelativeTime.tsx +6 -1
- package/src/SegmentBar.tsx +22 -11
- package/src/Segmented.tsx +16 -11
- package/src/Select.tsx +7 -4
- package/src/SelectSearch.tsx +3 -0
- package/src/ShortcutHints.tsx +24 -3
- package/src/Skeleton.tsx +1 -0
- package/src/Slider.tsx +4 -1
- package/src/SlidingColumn.tsx +18 -14
- package/src/Spinner.tsx +1 -0
- package/src/SplitButton.tsx +8 -8
- package/src/Stepper.tsx +6 -0
- package/src/Switch.tsx +2 -0
- package/src/TextMenu.tsx +2 -5
- package/src/Tile.tsx +2 -6
- package/src/Toasts.tsx +8 -12
- package/src/Tooltip.tsx +10 -5
- package/src/UIProvider.tsx +7 -7
- package/src/Waveform.tsx +41 -12
- package/src/Wipe.tsx +5 -6
- package/src/ZoomControls.tsx +5 -2
- package/src/clipboard.ts +4 -5
- package/src/combobox/parts.tsx +18 -14
- package/src/command-palette/context.ts +2 -0
- package/src/command-palette/index.parts.ts +1 -0
- package/src/command-palette/parts.tsx +20 -14
- package/src/command-palette/virtual.tsx +21 -0
- package/src/dialog/parts.tsx +10 -13
- package/src/dialog-layer.ts +5 -3
- package/src/error-boundary.ts +7 -20
- package/src/file-icon.ts +8 -9
- package/src/file-tree/empty-lines.ts +24 -8
- package/src/file-tree/helpers.ts +34 -0
- package/src/file-tree/highlight.ts +19 -0
- package/src/file-tree/index.parts.ts +1 -0
- package/src/file-tree/parts.tsx +41 -5
- package/src/file-tree/use-empty-lines.ts +22 -5
- package/src/file-tree/use-file-tree-shift.tsx +10 -0
- package/src/file-tree/use-file-tree.ts +2 -0
- package/src/file-tree/use-slots.ts +77 -12
- package/src/format/datetime.ts +42 -17
- package/src/format/duration.ts +28 -10
- package/src/format/index.ts +3 -0
- package/src/format/locale.ts +33 -42
- package/src/format/number.ts +21 -27
- package/src/format/regions.ts +4 -7
- package/src/icon-picker.ts +23 -16
- package/src/index.ts +9 -0
- package/src/key-value-list/parts.tsx +1 -5
- package/src/lazy-landings.ts +2 -0
- package/src/lazy.tsx +23 -19
- package/src/locales.ts +2 -6
- package/src/match.ts +14 -0
- package/src/menu/parts.tsx +30 -55
- package/src/middle-truncate.ts +12 -3
- package/src/modality.ts +4 -0
- package/src/placement.ts +2 -1
- package/src/platform.ts +2 -0
- package/src/popover/parts.tsx +3 -5
- package/src/popup-layer.ts +6 -4
- package/src/prefetch.ts +18 -5
- package/src/project-switcher-row.tsx +2 -0
- package/src/segmented-keys.ts +6 -0
- package/src/select-shared.ts +2 -0
- package/src/selection.ts +5 -5
- package/src/settings/ConfirmDialog.tsx +3 -0
- package/src/settings/MasterDetail.tsx +3 -9
- package/src/settings/SettingsDialog.tsx +27 -9
- package/src/settings/SettingsRow.tsx +16 -10
- package/src/settings/SettingsSection.tsx +10 -5
- package/src/settings/index.ts +3 -0
- package/src/settings/target.ts +1 -0
- package/src/shortcut-hints.ts +10 -5
- package/src/shortcut.ts +26 -6
- package/src/tabs/overflow.ts +8 -6
- package/src/tabs/parts.tsx +50 -26
- package/src/testing/dedupe.ts +27 -7
- package/src/testing/fake-source.ts +1 -0
- package/src/tint.ts +13 -0
- package/src/toast-store.ts +20 -14
- package/src/tree/parts.tsx +8 -0
- package/src/tree/shift.ts +6 -0
- package/src/tree/use-tree-shift.tsx +23 -7
- package/src/useAsyncAction.ts +8 -0
- package/src/useColumnResize.ts +24 -15
- package/src/useContentSize.ts +5 -0
- package/src/useMeasuredWidth.ts +1 -0
- package/src/useNow.ts +6 -11
- package/src/workspace/DropPreview.tsx +3 -5
- package/src/workspace/SplitView.tsx +109 -0
- package/src/workspace/TabStrip.tsx +42 -0
- package/src/workspace/Workspace.tsx +5 -0
- package/src/workspace/context.ts +1 -0
- package/src/workspace/geometry.ts +49 -0
- package/src/workspace/model.ts +72 -0
- package/src/workspace/preview.ts +15 -15
|
@@ -5,20 +5,17 @@ const HIDE_AFTER = 900;
|
|
|
5
5
|
|
|
6
6
|
const NEAR_BOTTOM = 16;
|
|
7
7
|
|
|
8
|
-
/* Where a tree draws its rows, and how far a row reaches past the edge it may use. */
|
|
9
8
|
export interface TreeShiftSource {
|
|
10
9
|
/* The node whose changes add, remove or rename rows; null while the tree has not drawn it yet. */
|
|
11
10
|
root(frame: HTMLElement): ParentNode | null;
|
|
11
|
+
|
|
12
12
|
rows(root: ParentNode): readonly HTMLElement[];
|
|
13
|
+
|
|
13
14
|
/* How many pixels of the row lie past its edge at no shift; null for a row that is not laid out. */
|
|
14
15
|
need(row: HTMLElement, shift: number, gap: number, range: Range): number | null;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
/*
|
|
18
|
-
* Rows that are wider than the tree slide sideways together, by a shift every row reads from the
|
|
19
|
-
* frame's `--adecore-tree-shift`, with a thin bar that shows while a person scrolls or nears it. A
|
|
20
|
-
* shift instead of a scroll keeps every row's background and the vertical scroll where they are.
|
|
21
|
-
*/
|
|
18
|
+
/* A shift every row reads from `--adecore-tree-shift` instead of a scroll keeps every row's background and the vertical scroll where they are. */
|
|
22
19
|
export function useTreeShift(source: TreeShiftSource, resetKey?: string): { attach(node: HTMLElement | null): void; bar: ReactElement } {
|
|
23
20
|
const [frame, setFrame] = useState<HTMLElement | null>(null);
|
|
24
21
|
const [track, setTrack] = useState<HTMLDivElement | null>(null);
|
|
@@ -28,6 +25,7 @@ export function useTreeShift(source: TreeShiftSource, resetKey?: string): { atta
|
|
|
28
25
|
if (frame === null || track === null || thumb === null) {
|
|
29
26
|
return;
|
|
30
27
|
}
|
|
28
|
+
|
|
31
29
|
const range = document.createRange();
|
|
32
30
|
let root: ParentNode | null = null;
|
|
33
31
|
let exact = 0;
|
|
@@ -40,10 +38,12 @@ export function useTreeShift(source: TreeShiftSource, resetKey?: string): { atta
|
|
|
40
38
|
|
|
41
39
|
const draw = (): void => {
|
|
42
40
|
const shift = Math.round(exact);
|
|
41
|
+
|
|
43
42
|
if (shift !== drawn) {
|
|
44
43
|
drawn = shift;
|
|
45
44
|
frame.style.setProperty(SHIFT_PROPERTY, `${shift}px`);
|
|
46
45
|
}
|
|
46
|
+
|
|
47
47
|
const { left, width } = shiftThumb(shift, max, track.clientWidth, frame.clientWidth);
|
|
48
48
|
thumb.style.left = `${left}px`;
|
|
49
49
|
thumb.style.width = `${width}px`;
|
|
@@ -52,13 +52,17 @@ export function useTreeShift(source: TreeShiftSource, resetKey?: string): { atta
|
|
|
52
52
|
|
|
53
53
|
const measure = (): void => {
|
|
54
54
|
pending = 0;
|
|
55
|
+
|
|
55
56
|
if (root === null) {
|
|
56
57
|
root = source.root(frame);
|
|
58
|
+
|
|
57
59
|
if (root === null) {
|
|
58
60
|
return;
|
|
59
61
|
}
|
|
62
|
+
|
|
60
63
|
rows.observe(root, { childList: true, subtree: true, characterData: true });
|
|
61
64
|
}
|
|
65
|
+
|
|
62
66
|
const drawnRows = source.rows(root);
|
|
63
67
|
const gap = drawnRows.length === 0 ? 0 : Number.parseFloat(getComputedStyle(drawnRows[0]!).columnGap) || 0;
|
|
64
68
|
max = maxShift(drawnRows.map((row) => source.need(row, drawn, gap, range)).filter((need) => need !== null));
|
|
@@ -80,26 +84,33 @@ export function useTreeShift(source: TreeShiftSource, resetKey?: string): { atta
|
|
|
80
84
|
|
|
81
85
|
const onWheel = (event: WheelEvent): void => {
|
|
82
86
|
const delta = sidewaysDelta(event, frame.clientWidth);
|
|
87
|
+
|
|
83
88
|
if (delta === 0 || max === 0) {
|
|
84
89
|
return;
|
|
85
90
|
}
|
|
91
|
+
|
|
86
92
|
scrolling = true;
|
|
87
93
|
window.clearTimeout(hideTimer);
|
|
94
|
+
|
|
88
95
|
hideTimer = window.setTimeout(() => {
|
|
89
96
|
scrolling = false;
|
|
90
97
|
draw();
|
|
91
98
|
}, HIDE_AFTER);
|
|
99
|
+
|
|
92
100
|
const next = clampShift(exact + delta, max);
|
|
101
|
+
|
|
93
102
|
if (next !== exact) {
|
|
94
103
|
event.preventDefault();
|
|
95
104
|
exact = next;
|
|
96
105
|
}
|
|
106
|
+
|
|
97
107
|
draw();
|
|
98
108
|
};
|
|
99
109
|
|
|
100
110
|
const onPointerMove = (event: PointerEvent): void => {
|
|
101
111
|
const bottom = track.getBoundingClientRect().bottom;
|
|
102
112
|
const isNear = event.clientY >= bottom - NEAR_BOTTOM;
|
|
113
|
+
|
|
103
114
|
if (isNear !== near) {
|
|
104
115
|
near = isNear;
|
|
105
116
|
draw();
|
|
@@ -143,21 +154,26 @@ export function useTreeShift(source: TreeShiftSource, resetKey?: string): { atta
|
|
|
143
154
|
return { attach: setFrame, bar };
|
|
144
155
|
}
|
|
145
156
|
|
|
146
|
-
/*
|
|
157
|
+
/* Everything a row of `Tree.Root` holds slides, so a row reaches as far as its last part. */
|
|
147
158
|
export const TREE_ROWS: TreeShiftSource = {
|
|
148
159
|
root: (frame) => frame,
|
|
149
160
|
rows: (root) => [...root.querySelectorAll<HTMLElement>('.adecore-tree-row')],
|
|
150
161
|
need: (row, shift) => {
|
|
151
162
|
const box = row.getBoundingClientRect();
|
|
163
|
+
|
|
152
164
|
if (box.width === 0) {
|
|
153
165
|
return null;
|
|
154
166
|
}
|
|
167
|
+
|
|
155
168
|
const parts = [...row.children].filter((child) => !child.classList.contains('adecore-tree-guide'));
|
|
169
|
+
|
|
156
170
|
if (parts.length === 0) {
|
|
157
171
|
return null;
|
|
158
172
|
}
|
|
173
|
+
|
|
159
174
|
const reach = Math.max(...parts.map((part) => part.getBoundingClientRect().right));
|
|
160
175
|
const limit = box.right - (Number.parseFloat(getComputedStyle(row).paddingInlineEnd) || 0);
|
|
176
|
+
|
|
161
177
|
return shiftNeed(reach, limit, shift);
|
|
162
178
|
}
|
|
163
179
|
};
|
package/src/useAsyncAction.ts
CHANGED
|
@@ -4,10 +4,13 @@ import { messageOf } from './error-message.ts';
|
|
|
4
4
|
export interface AsyncAction {
|
|
5
5
|
busy: boolean;
|
|
6
6
|
failure: string | null;
|
|
7
|
+
|
|
7
8
|
/* Says whether the work went through, so a caller closes or steps on only on the way out. */
|
|
8
9
|
run(work: () => Promise<unknown>): Promise<boolean>;
|
|
10
|
+
|
|
9
11
|
/* A refusal a surface can see for itself, which reads in the same line as one that came back. */
|
|
10
12
|
fail(message: string): void;
|
|
13
|
+
|
|
11
14
|
clear(): void;
|
|
12
15
|
}
|
|
13
16
|
|
|
@@ -15,18 +18,23 @@ export interface AsyncAction {
|
|
|
15
18
|
export function useAsyncAction(fallback?: string): AsyncAction {
|
|
16
19
|
const [busy, setBusy] = useState(false);
|
|
17
20
|
const [failure, setFailure] = useState<string | null>(null);
|
|
21
|
+
|
|
18
22
|
const run = async (work: () => Promise<unknown>): Promise<boolean> => {
|
|
19
23
|
setBusy(true);
|
|
20
24
|
setFailure(null);
|
|
25
|
+
|
|
21
26
|
try {
|
|
22
27
|
await work();
|
|
28
|
+
|
|
23
29
|
return true;
|
|
24
30
|
} catch (e) {
|
|
25
31
|
setFailure(messageOf(e, fallback));
|
|
32
|
+
|
|
26
33
|
return false;
|
|
27
34
|
} finally {
|
|
28
35
|
setBusy(false);
|
|
29
36
|
}
|
|
30
37
|
};
|
|
38
|
+
|
|
31
39
|
return { busy, failure, run, fail: setFailure, clear: () => setFailure(null) };
|
|
32
40
|
}
|
package/src/useColumnResize.ts
CHANGED
|
@@ -1,37 +1,39 @@
|
|
|
1
1
|
import type { PointerEvent as ReactPointerEvent, RefObject } from 'react';
|
|
2
2
|
|
|
3
|
-
/*
|
|
4
|
-
the window's right edge grows as the pointer moves left; one that starts at its own left edge
|
|
5
|
-
grows as it moves right, and the same holds for a row between `top` and `bottom`. */
|
|
3
|
+
/* The edge the column hangs from, and with it the axis the drag runs along. A column pinned to the right edge grows as the pointer moves left. */
|
|
6
4
|
export type ColumnEdge = 'left' | 'right' | 'top' | 'bottom';
|
|
7
5
|
|
|
8
6
|
export interface ColumnResizeOptions {
|
|
9
|
-
/*
|
|
7
|
+
/* In whole pixels. */
|
|
10
8
|
size: number;
|
|
9
|
+
|
|
11
10
|
min: number;
|
|
12
11
|
from: ColumnEdge;
|
|
12
|
+
|
|
13
13
|
/* Read at drag time, so a window resize between two drags is taken into account. */
|
|
14
14
|
max?(): number;
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
/* Called on every move. */
|
|
16
17
|
onSize(size: number): void;
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
const DRAG_ENDS = ['pointerup', 'pointercancel', 'lostpointercapture'] as const;
|
|
21
|
+
|
|
19
22
|
export interface ColumnResize {
|
|
20
23
|
startResize(event: ReactPointerEvent<HTMLElement>): void;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
export const clampColumnSize = (options: Pick<ColumnResizeOptions, 'min' | 'max'>, size: number): number => {
|
|
24
27
|
const max = Math.max(options.min, options.max?.() ?? Number.MAX_SAFE_INTEGER);
|
|
28
|
+
|
|
25
29
|
return Math.max(options.min, Math.min(Math.round(size), max));
|
|
26
30
|
};
|
|
27
31
|
|
|
28
|
-
/* Where the pointer sits on the axis this edge belongs to. */
|
|
29
32
|
const along = (from: ColumnEdge, event: { clientX: number; clientY: number }): number => (from === 'left' || from === 'right' ? event.clientX : event.clientY);
|
|
30
33
|
|
|
31
34
|
/*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* the column turns off any transition on that size for as long as it lasts.
|
|
35
|
+
* The handle takes the pointer capture, so the drag survives leaving the few pixels it is wide, and
|
|
36
|
+
* `[data-resizing]` on the column turns off any transition on that size for as long as it lasts.
|
|
35
37
|
*/
|
|
36
38
|
export const useColumnResize = (ref: RefObject<HTMLElement | null>, options: ColumnResizeOptions): ColumnResize => {
|
|
37
39
|
const startResize = (event: ReactPointerEvent<HTMLElement>): void => {
|
|
@@ -44,25 +46,32 @@ export const useColumnResize = (ref: RefObject<HTMLElement | null>, options: Col
|
|
|
44
46
|
/* The far edge grows as the pointer comes towards it; the near edge grows as it goes away. */
|
|
45
47
|
const growsTowardsAnchor = options.from === 'right' || options.from === 'bottom';
|
|
46
48
|
column?.setAttribute('data-resizing', 'true');
|
|
49
|
+
|
|
47
50
|
const onMove = (move: PointerEvent): void => {
|
|
48
51
|
const delta = along(options.from, move) - start;
|
|
49
52
|
options.onSize(clampColumnSize(options, size + (growsTowardsAnchor ? -delta : delta)));
|
|
50
53
|
};
|
|
54
|
+
|
|
51
55
|
const onUp = (): void => {
|
|
52
56
|
handle.removeEventListener('pointermove', onMove);
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
|
|
58
|
+
for (const type of DRAG_ENDS) {
|
|
59
|
+
handle.removeEventListener(type, onUp);
|
|
60
|
+
}
|
|
61
|
+
|
|
56
62
|
if (handle.hasPointerCapture(event.pointerId)) {
|
|
57
63
|
handle.releasePointerCapture(event.pointerId);
|
|
58
64
|
}
|
|
65
|
+
|
|
59
66
|
column?.removeAttribute('data-resizing');
|
|
60
67
|
};
|
|
68
|
+
|
|
61
69
|
handle.setPointerCapture(event.pointerId);
|
|
62
70
|
handle.addEventListener('pointermove', onMove);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
|
|
72
|
+
for (const type of DRAG_ENDS) {
|
|
73
|
+
handle.addEventListener(type, onUp);
|
|
74
|
+
}
|
|
66
75
|
};
|
|
67
76
|
|
|
68
77
|
return { startResize };
|
package/src/useContentSize.ts
CHANGED
|
@@ -10,18 +10,23 @@ export function useContentSize(): [(node: HTMLElement | null) => void, ContentSi
|
|
|
10
10
|
const [size, setSize] = useState<ContentSize>({ width: 0, height: 0 });
|
|
11
11
|
const observer = useRef<ResizeObserver | null>(null);
|
|
12
12
|
useEffect(() => () => observer.current?.disconnect(), []);
|
|
13
|
+
|
|
13
14
|
const measure = (node: HTMLElement | null): void => {
|
|
14
15
|
observer.current?.disconnect();
|
|
16
|
+
|
|
15
17
|
if (node === null) {
|
|
16
18
|
return;
|
|
17
19
|
}
|
|
20
|
+
|
|
18
21
|
const update = (width: number, height: number): void => {
|
|
19
22
|
const next = { width: Math.round(width), height: Math.round(height) };
|
|
20
23
|
setSize((current) => (current.width === next.width && current.height === next.height ? current : next));
|
|
21
24
|
};
|
|
25
|
+
|
|
22
26
|
update(node.clientWidth, node.clientHeight);
|
|
23
27
|
observer.current = new ResizeObserver(([entry]) => update(entry!.contentRect.width, entry!.contentRect.height));
|
|
24
28
|
observer.current.observe(node);
|
|
25
29
|
};
|
|
30
|
+
|
|
26
31
|
return [measure, size];
|
|
27
32
|
}
|
package/src/useMeasuredWidth.ts
CHANGED
|
@@ -3,5 +3,6 @@ import { useContentSize } from './useContentSize.ts';
|
|
|
3
3
|
/* A plot draws in real pixels rather than a stretched view box, so what it draws lands on whole ones. */
|
|
4
4
|
export function useMeasuredWidth(): [(node: HTMLElement | null) => void, number] {
|
|
5
5
|
const [measure, size] = useContentSize();
|
|
6
|
+
|
|
6
7
|
return [measure, size.width];
|
|
7
8
|
}
|
package/src/useNow.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState, type RefObject } from 'react';
|
|
2
2
|
|
|
3
|
-
/*
|
|
4
|
-
* The clock a surface reads to say how long something has been running. One timer for the whole
|
|
5
|
-
* surface, ticking only while there is something to count, and starting at the time it was mounted
|
|
6
|
-
* so a panel that comes back mid-session does not read zero for a second first.
|
|
7
|
-
*/
|
|
3
|
+
/* Starts at the time it was mounted, so a panel that comes back mid-session does not read zero for a second first. */
|
|
8
4
|
export const useNow = (intervalMs: number, ticking = true): number => {
|
|
9
5
|
const [now, setNow] = useState(Date.now);
|
|
10
6
|
|
|
@@ -12,17 +8,16 @@ export const useNow = (intervalMs: number, ticking = true): number => {
|
|
|
12
8
|
if (!ticking) {
|
|
13
9
|
return;
|
|
14
10
|
}
|
|
11
|
+
|
|
15
12
|
const timer = window.setInterval(() => setNow(Date.now()), intervalMs);
|
|
13
|
+
|
|
16
14
|
return () => window.clearInterval(timer);
|
|
17
15
|
}, [intervalMs, ticking]);
|
|
18
16
|
|
|
19
17
|
return now;
|
|
20
18
|
};
|
|
21
19
|
|
|
22
|
-
/*
|
|
23
|
-
* A timer on one line of a long thread. The text is written straight into the node, because a chat
|
|
24
|
-
* that re-renders every second while an agent works re-renders every row it is drawing with it.
|
|
25
|
-
*/
|
|
20
|
+
/* Written straight into the node: a thread that re-renders every second re-renders every row it draws with it. */
|
|
26
21
|
export const useTickingText = (render: () => string, intervalMs = 1000): RefObject<HTMLSpanElement | null> => {
|
|
27
22
|
const ref = useRef<HTMLSpanElement>(null);
|
|
28
23
|
const latest = useRef(render);
|
|
@@ -35,11 +30,11 @@ export const useTickingText = (render: () => string, intervalMs = 1000): RefObje
|
|
|
35
30
|
|
|
36
31
|
useEffect(() => {
|
|
37
32
|
const timer = window.setInterval(tick, intervalMs);
|
|
33
|
+
|
|
38
34
|
return () => window.clearInterval(timer);
|
|
39
35
|
}, [intervalMs, tick]);
|
|
40
36
|
|
|
41
|
-
/* After every render as well, so
|
|
42
|
-
lands on it without waiting for the next tick. */
|
|
37
|
+
/* After every render as well, so a language or region change lands without waiting for the next tick. */
|
|
43
38
|
useEffect(() => {
|
|
44
39
|
latest.current = render;
|
|
45
40
|
tick();
|
|
@@ -5,19 +5,17 @@ import { dropPreviewPath, type DropPreviewShape } from './preview.ts';
|
|
|
5
5
|
export interface DropPreviewProps {
|
|
6
6
|
/* Where the drop would land, in the coordinates of the box the preview fills; null draws nothing. */
|
|
7
7
|
shape: DropPreviewShape | null;
|
|
8
|
+
|
|
8
9
|
className?: string;
|
|
9
10
|
ref?: Ref<SVGSVGElement>;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
/*
|
|
13
|
-
* The outline `SplitView` draws where a drag would land, for an app with a drag layer of its own. It
|
|
14
|
-
* fills its nearest positioned ancestor. One shape moves into the next as a transition of its path,
|
|
15
|
-
* and the first shape after a null appears in place.
|
|
16
|
-
*/
|
|
13
|
+
/* For an app with a drag layer of its own. It fills its nearest positioned ancestor; the first shape after a null appears in place. */
|
|
17
14
|
export function DropPreview({ shape, className, ref }: DropPreviewProps) {
|
|
18
15
|
if (shape === null) {
|
|
19
16
|
return null;
|
|
20
17
|
}
|
|
18
|
+
|
|
21
19
|
return (
|
|
22
20
|
<svg ref={ref} className={clsx('ade-drop-preview', className)} width="100%" height="100%" aria-hidden="true">
|
|
23
21
|
<path style={{ d: `path("${dropPreviewPath(shape)}")` }} />
|