@adecore/ui 0.14.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/LICENSE +21 -0
- package/README.md +83 -0
- package/dist/AccentSwatches.d.ts +15 -0
- package/dist/AccentSwatches.js +21 -0
- package/dist/Banner.d.ts +12 -0
- package/dist/Banner.js +18 -0
- package/dist/Button.d.ts +9 -0
- package/dist/Button.js +28 -0
- package/dist/ButtonGroup.d.ts +3 -0
- package/dist/ButtonGroup.js +11 -0
- package/dist/Checkbox.d.ts +11 -0
- package/dist/Checkbox.js +9 -0
- package/dist/ChoiceCards.d.ts +23 -0
- package/dist/ChoiceCards.js +21 -0
- package/dist/CloseButton.d.ts +15 -0
- package/dist/CloseButton.js +11 -0
- package/dist/ColorSwatch.d.ts +14 -0
- package/dist/ColorSwatch.js +28 -0
- package/dist/ColumnResizeHandle.d.ts +9 -0
- package/dist/ColumnResizeHandle.js +12 -0
- package/dist/DisabledReason.d.ts +6 -0
- package/dist/DisabledReason.js +8 -0
- package/dist/DockShell.d.ts +9 -0
- package/dist/DockShell.js +49 -0
- package/dist/EmptyState.d.ts +16 -0
- package/dist/EmptyState.js +11 -0
- package/dist/ErrorBoundary.d.ts +24 -0
- package/dist/ErrorBoundary.js +47 -0
- package/dist/Field.d.ts +17 -0
- package/dist/Field.js +48 -0
- package/dist/FileIcon.d.ts +8 -0
- package/dist/FileIcon.js +15 -0
- package/dist/Icon.d.ts +9 -0
- package/dist/Icon.js +12 -0
- package/dist/IconButton.d.ts +23 -0
- package/dist/IconButton.js +25 -0
- package/dist/IconPicker.d.ts +17 -0
- package/dist/IconPicker.js +93 -0
- package/dist/Input.d.ts +14 -0
- package/dist/Input.js +25 -0
- package/dist/Kbd.d.ts +23 -0
- package/dist/Kbd.js +20 -0
- package/dist/ListRow.d.ts +10 -0
- package/dist/ListRow.js +20 -0
- package/dist/Meter.d.ts +12 -0
- package/dist/Meter.js +7 -0
- package/dist/PanelEmpty.d.ts +16 -0
- package/dist/PanelEmpty.js +7 -0
- package/dist/PanelHeader.d.ts +6 -0
- package/dist/PanelHeader.js +20 -0
- package/dist/Pill.d.ts +27 -0
- package/dist/Pill.js +25 -0
- package/dist/ProjectSwitcher.d.ts +30 -0
- package/dist/ProjectSwitcher.js +18 -0
- package/dist/PromptDialog.d.ts +40 -0
- package/dist/PromptDialog.js +47 -0
- package/dist/SectionLabel.d.ts +3 -0
- package/dist/SectionLabel.js +16 -0
- package/dist/SegmentBar.d.ts +18 -0
- package/dist/SegmentBar.js +44 -0
- package/dist/Segmented.d.ts +17 -0
- package/dist/Segmented.js +7 -0
- package/dist/Select.d.ts +27 -0
- package/dist/Select.js +34 -0
- package/dist/Separator.d.ts +7 -0
- package/dist/Separator.js +8 -0
- package/dist/ShortcutHints.d.ts +1 -0
- package/dist/ShortcutHints.js +94 -0
- package/dist/Skeleton.d.ts +6 -0
- package/dist/Skeleton.js +6 -0
- package/dist/SlidingColumn.d.ts +19 -0
- package/dist/SlidingColumn.js +41 -0
- package/dist/Spinner.d.ts +6 -0
- package/dist/Spinner.js +7 -0
- package/dist/Stepper.d.ts +14 -0
- package/dist/Stepper.js +18 -0
- package/dist/Surface.d.ts +3 -0
- package/dist/Surface.js +16 -0
- package/dist/Switch.d.ts +10 -0
- package/dist/Switch.js +7 -0
- package/dist/TextMenu.d.ts +5 -0
- package/dist/TextMenu.js +31 -0
- package/dist/Tile.d.ts +11 -0
- package/dist/Tile.js +10 -0
- package/dist/Toasts.d.ts +9 -0
- package/dist/Toasts.js +58 -0
- package/dist/Tooltip.d.ts +16 -0
- package/dist/Tooltip.js +21 -0
- package/dist/UIProvider.d.ts +9 -0
- package/dist/UIProvider.js +26 -0
- package/dist/Waveform.d.ts +16 -0
- package/dist/Waveform.js +123 -0
- package/dist/Wipe.d.ts +12 -0
- package/dist/Wipe.js +47 -0
- package/dist/ZoomControls.d.ts +30 -0
- package/dist/ZoomControls.js +16 -0
- package/dist/class-name.d.ts +2 -0
- package/dist/class-name.js +3 -0
- package/dist/clipboard.d.ts +2 -0
- package/dist/clipboard.js +16 -0
- package/dist/context-menu/index.parts.d.ts +1 -0
- package/dist/context-menu/index.parts.js +2 -0
- package/dist/dialog/index.parts.d.ts +1 -0
- package/dist/dialog/index.parts.js +1 -0
- package/dist/dialog/parts.d.ts +36 -0
- package/dist/dialog/parts.js +55 -0
- package/dist/dialog-layer.d.ts +1 -0
- package/dist/dialog-layer.js +27 -0
- package/dist/error-boundary.d.ts +4 -0
- package/dist/error-boundary.js +34 -0
- package/dist/error-message.d.ts +1 -0
- package/dist/error-message.js +4 -0
- package/dist/field-context.d.ts +7 -0
- package/dist/field-context.js +4 -0
- package/dist/file-icon.d.ts +4 -0
- package/dist/file-icon.js +46 -0
- package/dist/floating.d.ts +5 -0
- package/dist/floating.js +6 -0
- package/dist/format/datetime.d.ts +11 -0
- package/dist/format/datetime.js +50 -0
- package/dist/format/duration.d.ts +5 -0
- package/dist/format/duration.js +76 -0
- package/dist/format/index.d.ts +6 -0
- package/dist/format/index.js +7 -0
- package/dist/format/locale.d.ts +17 -0
- package/dist/format/locale.js +105 -0
- package/dist/format/number.d.ts +8 -0
- package/dist/format/number.js +72 -0
- package/dist/format/regions.d.ts +6 -0
- package/dist/format/regions.js +15 -0
- package/dist/format/time-zone.d.ts +1 -0
- package/dist/format/time-zone.js +2 -0
- package/dist/icon-button-size.d.ts +3 -0
- package/dist/icon-button-size.js +3 -0
- package/dist/icon-picker.d.ts +23 -0
- package/dist/icon-picker.js +80 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.js +74 -0
- package/dist/lazy.d.ts +12 -0
- package/dist/lazy.js +104 -0
- package/dist/locales/en.json +59 -0
- package/dist/locales/nl.json +59 -0
- package/dist/locales.d.ts +4 -0
- package/dist/locales.js +17 -0
- package/dist/menu/index.parts.d.ts +1 -0
- package/dist/menu/index.parts.js +1 -0
- package/dist/menu/parts.d.ts +67 -0
- package/dist/menu/parts.js +113 -0
- package/dist/merge-refs.d.ts +2 -0
- package/dist/merge-refs.js +11 -0
- package/dist/modality.d.ts +1 -0
- package/dist/modality.js +29 -0
- package/dist/platform.d.ts +2 -0
- package/dist/platform.js +9 -0
- package/dist/popover/index.parts.d.ts +1 -0
- package/dist/popover/index.parts.js +1 -0
- package/dist/popover/parts.d.ts +26 -0
- package/dist/popover/parts.js +28 -0
- package/dist/prefetch.d.ts +21 -0
- package/dist/prefetch.js +99 -0
- package/dist/preview-card/index.parts.d.ts +1 -0
- package/dist/preview-card/index.parts.js +1 -0
- package/dist/project-switcher-row.d.ts +6 -0
- package/dist/project-switcher-row.js +16 -0
- package/dist/selection.d.ts +2 -0
- package/dist/selection.js +22 -0
- package/dist/seti-icons.d.ts +5 -0
- package/dist/seti-icons.js +445 -0
- package/dist/settings/ConfirmDialog.d.ts +9 -0
- package/dist/settings/ConfirmDialog.js +9 -0
- package/dist/settings/DetailHeader.d.ts +10 -0
- package/dist/settings/DetailHeader.js +6 -0
- package/dist/settings/MasterDetail.d.ts +24 -0
- package/dist/settings/MasterDetail.js +16 -0
- package/dist/settings/SettingsDialog.d.ts +46 -0
- package/dist/settings/SettingsDialog.js +96 -0
- package/dist/settings/SettingsRow.d.ts +22 -0
- package/dist/settings/SettingsRow.js +30 -0
- package/dist/settings/SettingsSection.d.ts +14 -0
- package/dist/settings/SettingsSection.js +8 -0
- package/dist/settings/index.d.ts +7 -0
- package/dist/settings/index.js +8 -0
- package/dist/settings/target.d.ts +7 -0
- package/dist/settings/target.js +3 -0
- package/dist/shortcut-hints.d.ts +38 -0
- package/dist/shortcut-hints.js +81 -0
- package/dist/shortcut.d.ts +32 -0
- package/dist/shortcut.js +137 -0
- package/dist/testing/dedupe.d.ts +1 -0
- package/dist/testing/dedupe.js +73 -0
- package/dist/testing/fake-source.d.ts +8 -0
- package/dist/testing/fake-source.js +15 -0
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +2 -0
- package/dist/theme.css +562 -0
- package/dist/toast-store.d.ts +38 -0
- package/dist/toast-store.js +61 -0
- package/dist/useAsyncAction.d.ts +8 -0
- package/dist/useAsyncAction.js +23 -0
- package/dist/useColumnResize.d.ts +14 -0
- package/dist/useColumnResize.js +49 -0
- package/dist/useContentSize.d.ts +5 -0
- package/dist/useContentSize.js +21 -0
- package/dist/useMeasuredWidth.d.ts +1 -0
- package/dist/useMeasuredWidth.js +6 -0
- package/dist/useNow.d.ts +3 -0
- package/dist/useNow.js +41 -0
- package/dist/wipe-split.d.ts +7 -0
- package/dist/wipe-split.js +20 -0
- package/dist/zoom.d.ts +3 -0
- package/dist/zoom.js +6 -0
- package/package.json +92 -0
- package/src/AccentSwatches.tsx +73 -0
- package/src/Banner.tsx +43 -0
- package/src/Button.tsx +55 -0
- package/src/ButtonGroup.tsx +14 -0
- package/src/Checkbox.tsx +44 -0
- package/src/ChoiceCards.tsx +108 -0
- package/src/CloseButton.tsx +21 -0
- package/src/ColorSwatch.tsx +41 -0
- package/src/ColumnResizeHandle.tsx +23 -0
- package/src/DisabledReason.tsx +23 -0
- package/src/DockShell.tsx +76 -0
- package/src/EmptyState.tsx +48 -0
- package/src/ErrorBoundary.tsx +116 -0
- package/src/Field.tsx +116 -0
- package/src/FileIcon.tsx +27 -0
- package/src/Icon.tsx +22 -0
- package/src/IconButton.tsx +84 -0
- package/src/IconPicker.tsx +257 -0
- package/src/Input.tsx +78 -0
- package/src/Kbd.tsx +67 -0
- package/src/ListRow.tsx +26 -0
- package/src/Meter.tsx +41 -0
- package/src/PanelEmpty.tsx +37 -0
- package/src/PanelHeader.tsx +31 -0
- package/src/Pill.tsx +61 -0
- package/src/ProjectSwitcher.tsx +127 -0
- package/src/PromptDialog.tsx +168 -0
- package/src/SectionLabel.tsx +20 -0
- package/src/SegmentBar.tsx +128 -0
- package/src/Segmented.tsx +51 -0
- package/src/Select.tsx +135 -0
- package/src/Separator.tsx +15 -0
- package/src/ShortcutHints.tsx +118 -0
- package/src/Skeleton.tsx +13 -0
- package/src/SlidingColumn.tsx +91 -0
- package/src/Spinner.tsx +32 -0
- package/src/Stepper.tsx +67 -0
- package/src/Surface.tsx +20 -0
- package/src/Switch.tsx +32 -0
- package/src/TextMenu.tsx +59 -0
- package/src/Tile.tsx +49 -0
- package/src/Toasts.tsx +151 -0
- package/src/Tooltip.tsx +70 -0
- package/src/UIProvider.tsx +41 -0
- package/src/Waveform.tsx +199 -0
- package/src/Wipe.tsx +99 -0
- package/src/ZoomControls.tsx +99 -0
- package/src/class-name.ts +8 -0
- package/src/clipboard.ts +17 -0
- package/src/context-menu/index.parts.ts +21 -0
- package/src/dialog/index.parts.ts +10 -0
- package/src/dialog/parts.tsx +112 -0
- package/src/dialog-layer.ts +31 -0
- package/src/error-boundary.ts +40 -0
- package/src/error-message.ts +5 -0
- package/src/field-context.ts +12 -0
- package/src/file-icon.ts +52 -0
- package/src/floating.ts +9 -0
- package/src/format/datetime.ts +63 -0
- package/src/format/duration.ts +83 -0
- package/src/format/index.ts +20 -0
- package/src/format/locale.ts +153 -0
- package/src/format/number.ts +87 -0
- package/src/format/regions.ts +20 -0
- package/src/format/time-zone.ts +2 -0
- package/src/icon-button-size.ts +6 -0
- package/src/icon-picker.ts +112 -0
- package/src/index.ts +103 -0
- package/src/lazy.tsx +124 -0
- package/src/locales/en.json +59 -0
- package/src/locales/nl.json +59 -0
- package/src/locales.ts +21 -0
- package/src/menu/index.parts.ts +20 -0
- package/src/menu/parts.tsx +296 -0
- package/src/merge-refs.ts +14 -0
- package/src/modality.ts +37 -0
- package/src/platform.ts +10 -0
- package/src/popover/index.parts.ts +8 -0
- package/src/popover/parts.tsx +119 -0
- package/src/prefetch.ts +113 -0
- package/src/preview-card/index.parts.ts +1 -0
- package/src/project-switcher-row.tsx +51 -0
- package/src/selection.ts +24 -0
- package/src/seti-icons.ts +451 -0
- package/src/settings/ConfirmDialog.tsx +30 -0
- package/src/settings/DetailHeader.tsx +25 -0
- package/src/settings/MasterDetail.tsx +67 -0
- package/src/settings/SettingsDialog.tsx +342 -0
- package/src/settings/SettingsRow.tsx +89 -0
- package/src/settings/SettingsSection.tsx +46 -0
- package/src/settings/index.ts +16 -0
- package/src/settings/target.ts +12 -0
- package/src/shortcut-hints.ts +117 -0
- package/src/shortcut.ts +170 -0
- package/src/testing/dedupe.ts +93 -0
- package/src/testing/fake-source.ts +21 -0
- package/src/testing/index.ts +3 -0
- package/src/theme.css +562 -0
- package/src/toast-store.ts +120 -0
- package/src/useAsyncAction.ts +32 -0
- package/src/useColumnResize.ts +75 -0
- package/src/useContentSize.ts +27 -0
- package/src/useMeasuredWidth.ts +7 -0
- package/src/useNow.ts +49 -0
- package/src/wipe-split.ts +24 -0
- package/src/zoom.ts +9 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { messageOf } from './error-message.ts';
|
|
3
|
+
|
|
4
|
+
export interface AsyncAction {
|
|
5
|
+
busy: boolean;
|
|
6
|
+
failure: string | null;
|
|
7
|
+
/* Says whether the work went through, so a caller closes or steps on only on the way out. */
|
|
8
|
+
run(work: () => Promise<unknown>): Promise<boolean>;
|
|
9
|
+
/* A refusal a surface can see for itself, which reads in the same line as one that came back. */
|
|
10
|
+
fail(message: string): void;
|
|
11
|
+
clear(): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* One step a surface waits on: the button goes quiet while it runs and the reason stays on screen when it does not. */
|
|
15
|
+
export function useAsyncAction(fallback?: string): AsyncAction {
|
|
16
|
+
const [busy, setBusy] = useState(false);
|
|
17
|
+
const [failure, setFailure] = useState<string | null>(null);
|
|
18
|
+
const run = async (work: () => Promise<unknown>): Promise<boolean> => {
|
|
19
|
+
setBusy(true);
|
|
20
|
+
setFailure(null);
|
|
21
|
+
try {
|
|
22
|
+
await work();
|
|
23
|
+
return true;
|
|
24
|
+
} catch (e) {
|
|
25
|
+
setFailure(messageOf(e, fallback));
|
|
26
|
+
return false;
|
|
27
|
+
} finally {
|
|
28
|
+
setBusy(false);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return { busy, failure, run, fail: setFailure, clear: () => setFailure(null) };
|
|
32
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { PointerEvent as ReactPointerEvent, RefObject } from 'react';
|
|
2
|
+
|
|
3
|
+
/* Which edge the column hangs from, and with it the axis the drag runs along. A column pinned to
|
|
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`. */
|
|
6
|
+
export type ColumnEdge = 'left' | 'right' | 'top' | 'bottom';
|
|
7
|
+
|
|
8
|
+
export interface ColumnResizeOptions {
|
|
9
|
+
/* What the column is wide, or the row is tall, right now, in whole pixels. */
|
|
10
|
+
size: number;
|
|
11
|
+
min: number;
|
|
12
|
+
from: ColumnEdge;
|
|
13
|
+
/* Read at drag time, so a window resize between two drags is taken into account. */
|
|
14
|
+
max?(): number;
|
|
15
|
+
/* Where the dragged size goes; called on every move, so the column follows the pointer. */
|
|
16
|
+
onSize(size: number): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ColumnResize {
|
|
20
|
+
startResize(event: ReactPointerEvent<HTMLElement>): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const clampColumnSize = (options: Pick<ColumnResizeOptions, 'min' | 'max'>, size: number): number => {
|
|
24
|
+
const max = Math.max(options.min, options.max?.() ?? Number.MAX_SAFE_INTEGER);
|
|
25
|
+
return Math.max(options.min, Math.min(Math.round(size), max));
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/* Where the pointer sits on the axis this edge belongs to. */
|
|
29
|
+
const along = (from: ColumnEdge, event: { clientX: number; clientY: number }): number => (from === 'left' || from === 'right' ? event.clientX : event.clientY);
|
|
30
|
+
|
|
31
|
+
/* The edge the column is pinned to, in page coordinates, or the window's own when there is no box yet. */
|
|
32
|
+
const anchorOf = (from: ColumnEdge, rect: DOMRect | undefined): number => {
|
|
33
|
+
switch (from) {
|
|
34
|
+
case 'left':
|
|
35
|
+
return rect?.left ?? 0;
|
|
36
|
+
case 'right':
|
|
37
|
+
return rect?.right ?? window.innerWidth;
|
|
38
|
+
case 'top':
|
|
39
|
+
return rect?.top ?? 0;
|
|
40
|
+
case 'bottom':
|
|
41
|
+
return rect?.bottom ?? window.innerHeight;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/*
|
|
46
|
+
* One resizable column or row: a handle that drags the size its owner keeps. The handle takes the
|
|
47
|
+
* pointer capture, so the drag survives leaving the few pixels it is wide, and `[data-resizing]` on
|
|
48
|
+
* the column turns off any transition on that size for as long as it lasts.
|
|
49
|
+
*/
|
|
50
|
+
export const useColumnResize = (ref: RefObject<HTMLElement | null>, options: ColumnResizeOptions): ColumnResize => {
|
|
51
|
+
const startResize = (event: ReactPointerEvent<HTMLElement>): void => {
|
|
52
|
+
event.preventDefault();
|
|
53
|
+
const handle = event.currentTarget;
|
|
54
|
+
const column = ref.current;
|
|
55
|
+
const anchor = anchorOf(options.from, column?.getBoundingClientRect());
|
|
56
|
+
/* The far edge grows as the pointer comes towards it; the near edge grows as it goes away. */
|
|
57
|
+
const growsTowardsAnchor = options.from === 'right' || options.from === 'bottom';
|
|
58
|
+
column?.setAttribute('data-resizing', 'true');
|
|
59
|
+
const onMove = (move: PointerEvent): void => {
|
|
60
|
+
const reach = along(options.from, move);
|
|
61
|
+
options.onSize(clampColumnSize(options, growsTowardsAnchor ? anchor - reach : reach - anchor));
|
|
62
|
+
};
|
|
63
|
+
const onUp = (): void => {
|
|
64
|
+
handle.removeEventListener('pointermove', onMove);
|
|
65
|
+
handle.removeEventListener('pointerup', onUp);
|
|
66
|
+
handle.releasePointerCapture(event.pointerId);
|
|
67
|
+
column?.removeAttribute('data-resizing');
|
|
68
|
+
};
|
|
69
|
+
handle.setPointerCapture(event.pointerId);
|
|
70
|
+
handle.addEventListener('pointermove', onMove);
|
|
71
|
+
handle.addEventListener('pointerup', onUp);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return { startResize };
|
|
75
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface ContentSize {
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* Whole pixels, so a frame fitted into the space lands on them too. */
|
|
9
|
+
export function useContentSize(): [(node: HTMLElement | null) => void, ContentSize] {
|
|
10
|
+
const [size, setSize] = useState<ContentSize>({ width: 0, height: 0 });
|
|
11
|
+
const observer = useRef<ResizeObserver | null>(null);
|
|
12
|
+
useEffect(() => () => observer.current?.disconnect(), []);
|
|
13
|
+
const measure = useCallback((node: HTMLElement | null): void => {
|
|
14
|
+
observer.current?.disconnect();
|
|
15
|
+
if (node === null) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const update = (width: number, height: number): void => {
|
|
19
|
+
const next = { width: Math.round(width), height: Math.round(height) };
|
|
20
|
+
setSize((current) => (current.width === next.width && current.height === next.height ? current : next));
|
|
21
|
+
};
|
|
22
|
+
update(node.clientWidth, node.clientHeight);
|
|
23
|
+
observer.current = new ResizeObserver(([entry]) => update(entry!.contentRect.width, entry!.contentRect.height));
|
|
24
|
+
observer.current.observe(node);
|
|
25
|
+
}, []);
|
|
26
|
+
return [measure, size];
|
|
27
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { useContentSize } from './useContentSize.ts';
|
|
2
|
+
|
|
3
|
+
/* A plot draws in real pixels rather than a stretched view box, so what it draws lands on whole ones. */
|
|
4
|
+
export function useMeasuredWidth(): [(node: HTMLElement | null) => void, number] {
|
|
5
|
+
const [measure, size] = useContentSize();
|
|
6
|
+
return [measure, size.width];
|
|
7
|
+
}
|
package/src/useNow.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState, type RefObject } from 'react';
|
|
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
|
+
*/
|
|
8
|
+
export const useNow = (intervalMs: number, ticking = true): number => {
|
|
9
|
+
const [now, setNow] = useState(Date.now);
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (!ticking) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const timer = window.setInterval(() => setNow(Date.now()), intervalMs);
|
|
16
|
+
return () => window.clearInterval(timer);
|
|
17
|
+
}, [intervalMs, ticking]);
|
|
18
|
+
|
|
19
|
+
return now;
|
|
20
|
+
};
|
|
21
|
+
|
|
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
|
+
*/
|
|
26
|
+
export const useTickingText = (render: () => string, intervalMs = 1000): RefObject<HTMLSpanElement | null> => {
|
|
27
|
+
const ref = useRef<HTMLSpanElement>(null);
|
|
28
|
+
const latest = useRef(render);
|
|
29
|
+
|
|
30
|
+
const tick = useCallback((): void => {
|
|
31
|
+
if (ref.current !== null) {
|
|
32
|
+
ref.current.textContent = latest.current();
|
|
33
|
+
}
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const timer = window.setInterval(tick, intervalMs);
|
|
38
|
+
return () => window.clearInterval(timer);
|
|
39
|
+
}, [intervalMs, tick]);
|
|
40
|
+
|
|
41
|
+
/* After every render as well, so the first one writes the line and a language or region change
|
|
42
|
+
lands on it without waiting for the next tick. */
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
latest.current = render;
|
|
45
|
+
tick();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return ref;
|
|
49
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* One press of an arrow moves the handle this share of the frame. */
|
|
2
|
+
export const WIPE_KEY_STEP = 0.02;
|
|
3
|
+
|
|
4
|
+
export const clampSplit = (split: number): number => Math.min(1, Math.max(0, split));
|
|
5
|
+
|
|
6
|
+
/* A frame without a width has no place to point at, so the split stays where it is. */
|
|
7
|
+
export const splitAt = (clientX: number, frame: { left: number; width: number }): number | null =>
|
|
8
|
+
frame.width > 0 ? clampSplit((clientX - frame.left) / frame.width) : null;
|
|
9
|
+
|
|
10
|
+
/* Null for a key the handle does not answer, so it goes on to whatever is around it. */
|
|
11
|
+
export const splitForKey = (split: number, key: string): number | null => {
|
|
12
|
+
switch (key) {
|
|
13
|
+
case 'ArrowLeft':
|
|
14
|
+
return clampSplit(split - WIPE_KEY_STEP);
|
|
15
|
+
case 'ArrowRight':
|
|
16
|
+
return clampSplit(split + WIPE_KEY_STEP);
|
|
17
|
+
case 'Home':
|
|
18
|
+
return 0;
|
|
19
|
+
case 'End':
|
|
20
|
+
return 1;
|
|
21
|
+
default:
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
};
|
package/src/zoom.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* The percentages a zoom menu offers when the app names none. */
|
|
2
|
+
export const ZOOM_PRESETS: readonly number[] = [25, 50, 75, 100, 150, 200];
|
|
3
|
+
|
|
4
|
+
/* Compared on the rounded percent the readout shows, so 0.999 still ticks 100%. */
|
|
5
|
+
export const activeZoomPreset = (zoom: number, presets: readonly number[]): number | null =>
|
|
6
|
+
presets.find((preset) => preset === Math.round(zoom * 100)) ?? null;
|
|
7
|
+
|
|
8
|
+
/* Ten percent a click, on the readout's own rounded percent, so + and - come back where they were. */
|
|
9
|
+
export const steppedZoom = (zoom: number, by: number): number => Math.round(zoom * 100 + by) / 100;
|