@liberation-data/desk 0.1.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 +201 -0
- package/NOTICE +4 -0
- package/README.md +436 -0
- package/dist/core/commands.d.ts +40 -0
- package/dist/core/commands.d.ts.map +1 -0
- package/dist/core/commands.js +92 -0
- package/dist/core/commands.js.map +1 -0
- package/dist/core/desk.d.ts +46 -0
- package/dist/core/desk.d.ts.map +1 -0
- package/dist/core/desk.js +178 -0
- package/dist/core/desk.js.map +1 -0
- package/dist/core/events.d.ts +42 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +54 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +7 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/location.d.ts +30 -0
- package/dist/core/location.d.ts.map +1 -0
- package/dist/core/location.js +99 -0
- package/dist/core/location.js.map +1 -0
- package/dist/core/shortcuts.d.ts +36 -0
- package/dist/core/shortcuts.d.ts.map +1 -0
- package/dist/core/shortcuts.js +73 -0
- package/dist/core/shortcuts.js.map +1 -0
- package/dist/core/titles.d.ts +3 -0
- package/dist/core/titles.d.ts.map +1 -0
- package/dist/core/titles.js +26 -0
- package/dist/core/titles.js.map +1 -0
- package/dist/core/types.d.ts +51 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/desk.css +2550 -0
- package/dist/react/Desktop.d.ts +24 -0
- package/dist/react/Desktop.d.ts.map +1 -0
- package/dist/react/Desktop.js +212 -0
- package/dist/react/Desktop.js.map +1 -0
- package/dist/react/Dock.d.ts +42 -0
- package/dist/react/Dock.d.ts.map +1 -0
- package/dist/react/Dock.js +103 -0
- package/dist/react/Dock.js.map +1 -0
- package/dist/react/MenuBar.d.ts +64 -0
- package/dist/react/MenuBar.d.ts.map +1 -0
- package/dist/react/MenuBar.js +245 -0
- package/dist/react/MenuBar.js.map +1 -0
- package/dist/react/appFrame.d.ts +37 -0
- package/dist/react/appFrame.d.ts.map +1 -0
- package/dist/react/appFrame.js +119 -0
- package/dist/react/appFrame.js.map +1 -0
- package/dist/react/arrange.d.ts +24 -0
- package/dist/react/arrange.d.ts.map +1 -0
- package/dist/react/arrange.js +61 -0
- package/dist/react/arrange.js.map +1 -0
- package/dist/react/commands.d.ts +23 -0
- package/dist/react/commands.d.ts.map +1 -0
- package/dist/react/commands.js +41 -0
- package/dist/react/commands.js.map +1 -0
- package/dist/react/context.d.ts +25 -0
- package/dist/react/context.d.ts.map +1 -0
- package/dist/react/context.js +28 -0
- package/dist/react/context.js.map +1 -0
- package/dist/react/controls.d.ts +111 -0
- package/dist/react/controls.d.ts.map +1 -0
- package/dist/react/controls.js +83 -0
- package/dist/react/controls.js.map +1 -0
- package/dist/react/conversation.d.ts +41 -0
- package/dist/react/conversation.d.ts.map +1 -0
- package/dist/react/conversation.js +73 -0
- package/dist/react/conversation.js.map +1 -0
- package/dist/react/dnd.d.ts +51 -0
- package/dist/react/dnd.d.ts.map +1 -0
- package/dist/react/dnd.js +138 -0
- package/dist/react/dnd.js.map +1 -0
- package/dist/react/dragContext.d.ts +32 -0
- package/dist/react/dragContext.d.ts.map +1 -0
- package/dist/react/dragContext.js +14 -0
- package/dist/react/dragContext.js.map +1 -0
- package/dist/react/events.d.ts +41 -0
- package/dist/react/events.d.ts.map +1 -0
- package/dist/react/events.js +91 -0
- package/dist/react/events.js.map +1 -0
- package/dist/react/index.d.ts +43 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +22 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/layers.d.ts +5 -0
- package/dist/react/layers.d.ts.map +1 -0
- package/dist/react/layers.js +65 -0
- package/dist/react/layers.js.map +1 -0
- package/dist/react/overlays.d.ts +67 -0
- package/dist/react/overlays.d.ts.map +1 -0
- package/dist/react/overlays.js +98 -0
- package/dist/react/overlays.js.map +1 -0
- package/dist/react/popup.d.ts +21 -0
- package/dist/react/popup.d.ts.map +1 -0
- package/dist/react/popup.js +94 -0
- package/dist/react/popup.js.map +1 -0
- package/dist/react/search.d.ts +33 -0
- package/dist/react/search.d.ts.map +1 -0
- package/dist/react/search.js +82 -0
- package/dist/react/search.js.map +1 -0
- package/dist/react/setupProgress.d.ts +35 -0
- package/dist/react/setupProgress.d.ts.map +1 -0
- package/dist/react/setupProgress.js +68 -0
- package/dist/react/setupProgress.js.map +1 -0
- package/dist/react/shell.d.ts +17 -0
- package/dist/react/shell.d.ts.map +1 -0
- package/dist/react/shell.js +13 -0
- package/dist/react/shell.js.map +1 -0
- package/dist/react/sidebar.d.ts +23 -0
- package/dist/react/sidebar.d.ts.map +1 -0
- package/dist/react/sidebar.js +26 -0
- package/dist/react/sidebar.js.map +1 -0
- package/dist/react/table.d.ts +39 -0
- package/dist/react/table.d.ts.map +1 -0
- package/dist/react/table.js +86 -0
- package/dist/react/table.js.map +1 -0
- package/dist/react/tasks.d.ts +41 -0
- package/dist/react/tasks.d.ts.map +1 -0
- package/dist/react/tasks.js +79 -0
- package/dist/react/tasks.js.map +1 -0
- package/dist/react/tour.d.ts +42 -0
- package/dist/react/tour.d.ts.map +1 -0
- package/dist/react/tour.js +68 -0
- package/dist/react/tour.js.map +1 -0
- package/dist/react/windowBoundary.d.ts +22 -0
- package/dist/react/windowBoundary.d.ts.map +1 -0
- package/dist/react/windowBoundary.js +50 -0
- package/dist/react/windowBoundary.js.map +1 -0
- package/dist/react/wizard.d.ts +41 -0
- package/dist/react/wizard.d.ts.map +1 -0
- package/dist/react/wizard.js +60 -0
- package/dist/react/wizard.js.map +1 -0
- package/llms.txt +410 -0
- package/package.json +64 -0
- package/src/core/commands.ts +142 -0
- package/src/core/desk.ts +229 -0
- package/src/core/events.ts +99 -0
- package/src/core/index.ts +43 -0
- package/src/core/location.ts +127 -0
- package/src/core/shortcuts.ts +119 -0
- package/src/core/titles.ts +28 -0
- package/src/core/types.ts +49 -0
- package/src/desk.css +2550 -0
- package/src/react/Desktop.tsx +349 -0
- package/src/react/Dock.tsx +258 -0
- package/src/react/MenuBar.tsx +350 -0
- package/src/react/appFrame.tsx +183 -0
- package/src/react/arrange.ts +82 -0
- package/src/react/commands.ts +60 -0
- package/src/react/context.tsx +55 -0
- package/src/react/controls.tsx +364 -0
- package/src/react/conversation.tsx +208 -0
- package/src/react/dnd.tsx +189 -0
- package/src/react/dragContext.tsx +60 -0
- package/src/react/events.tsx +149 -0
- package/src/react/index.ts +64 -0
- package/src/react/layers.ts +66 -0
- package/src/react/overlays.tsx +254 -0
- package/src/react/popup.tsx +176 -0
- package/src/react/search.tsx +177 -0
- package/src/react/setupProgress.ts +113 -0
- package/src/react/shell.tsx +27 -0
- package/src/react/sidebar.tsx +84 -0
- package/src/react/table.tsx +176 -0
- package/src/react/tasks.tsx +176 -0
- package/src/react/tour.tsx +183 -0
- package/src/react/windowBoundary.tsx +110 -0
- package/src/react/wizard.tsx +150 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ReactNode, RefObject } from 'react';
|
|
2
|
+
import type { Bus } from '../core/events.js';
|
|
3
|
+
import type { Desk } from '../core/desk.js';
|
|
4
|
+
import type { DeskOptions, DeskState, WindowId } from '../core/types.js';
|
|
5
|
+
/** Every desk comes with a bus; <BusProvider> only matters when one has to be shared or supplied. */
|
|
6
|
+
export declare const BusContext: import("react").Context<Bus | null>;
|
|
7
|
+
export interface DeskProviderProps {
|
|
8
|
+
/** An existing desk, e.g. one a tour runner or router also drives. */
|
|
9
|
+
readonly desk?: Desk;
|
|
10
|
+
/** Used to create a desk when none is given. Read once. */
|
|
11
|
+
readonly options?: DeskOptions;
|
|
12
|
+
readonly children: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
export declare function DeskProvider({ desk, options, children }: DeskProviderProps): import("react").JSX.Element;
|
|
15
|
+
export declare function useDesk(): Desk;
|
|
16
|
+
export declare function useDeskState(): DeskState;
|
|
17
|
+
export interface WindowContextValue {
|
|
18
|
+
readonly id: WindowId;
|
|
19
|
+
readonly element: RefObject<HTMLElement | null>;
|
|
20
|
+
}
|
|
21
|
+
/** Present inside a window's content: which window this is, and its element. */
|
|
22
|
+
export declare const WindowContext: import("react").Context<WindowContextValue | null>;
|
|
23
|
+
/** The id of the window this component is rendered in, or null outside any window. */
|
|
24
|
+
export declare const useWindowId: () => WindowId | null;
|
|
25
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/react/context.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AAG5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAIxE,qGAAqG;AACrG,eAAO,MAAM,UAAU,qCAAkC,CAAA;AAEzD,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAA;IACpB,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAA;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAC7B;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,iBAAiB,+BAU1E;AAED,wBAAgB,OAAO,IAAI,IAAI,CAI9B;AAED,wBAAgB,YAAY,IAAI,SAAS,CAGxC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;CAChD;AAED,gFAAgF;AAChF,eAAO,MAAM,aAAa,oDAAiD,CAAA;AAE3E,sFAAsF;AACtF,eAAO,MAAM,WAAW,QAAO,QAAQ,GAAG,IAA6C,CAAA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useState, useSyncExternalStore } from 'react';
|
|
3
|
+
import { createBus } from '../core/events.js';
|
|
4
|
+
import { createDesk } from '../core/desk.js';
|
|
5
|
+
import { DragProvider } from './dragContext.js';
|
|
6
|
+
const DeskContext = createContext(null);
|
|
7
|
+
/** Every desk comes with a bus; <BusProvider> only matters when one has to be shared or supplied. */
|
|
8
|
+
export const BusContext = createContext(null);
|
|
9
|
+
export function DeskProvider({ desk, options, children }) {
|
|
10
|
+
const [value] = useState(() => desk ?? createDesk(options));
|
|
11
|
+
const [bus] = useState(createBus);
|
|
12
|
+
return (_jsx(DeskContext.Provider, { value: value, children: _jsx(BusContext.Provider, { value: bus, children: _jsx(DragProvider, { children: children }) }) }));
|
|
13
|
+
}
|
|
14
|
+
export function useDesk() {
|
|
15
|
+
const desk = useContext(DeskContext);
|
|
16
|
+
if (!desk)
|
|
17
|
+
throw new Error('useDesk must be used inside a <DeskProvider>');
|
|
18
|
+
return desk;
|
|
19
|
+
}
|
|
20
|
+
export function useDeskState() {
|
|
21
|
+
const desk = useDesk();
|
|
22
|
+
return useSyncExternalStore(desk.subscribe, desk.getState, desk.getState);
|
|
23
|
+
}
|
|
24
|
+
/** Present inside a window's content: which window this is, and its element. */
|
|
25
|
+
export const WindowContext = createContext(null);
|
|
26
|
+
/** The id of the window this component is rendered in, or null outside any window. */
|
|
27
|
+
export const useWindowId = () => useContext(WindowContext)?.id ?? null;
|
|
28
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/react/context.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAEjF,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAI/C,MAAM,WAAW,GAAG,aAAa,CAAc,IAAI,CAAC,CAAA;AAEpD,qGAAqG;AACrG,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAa,IAAI,CAAC,CAAA;AAUzD,MAAM,UAAU,YAAY,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAqB;IACzE,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC,CAAA;IAC3D,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAA;IACjC,OAAO,CACL,KAAC,WAAW,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAChC,KAAC,UAAU,CAAC,QAAQ,IAAC,KAAK,EAAE,GAAG,YAC7B,KAAC,YAAY,cAAE,QAAQ,GAAgB,GACnB,GACD,CACxB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;IACpC,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAA;IAC1E,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,IAAI,GAAG,OAAO,EAAE,CAAA;IACtB,OAAO,oBAAoB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;AAC3E,CAAC;AAOD,gFAAgF;AAChF,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAA4B,IAAI,CAAC,CAAA;AAE3E,sFAAsF;AACtF,MAAM,CAAC,MAAM,WAAW,GAAG,GAAoB,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,EAAE,IAAI,IAAI,CAAA"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes, InputHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
export type ButtonIntent = 'normal' | 'default' | 'quiet' | 'destructive';
|
|
3
|
+
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
4
|
+
/** `default` is the one action a view leads with, and the one Return performs. One per view. */
|
|
5
|
+
readonly intent?: ButtonIntent;
|
|
6
|
+
readonly size?: 'regular' | 'small';
|
|
7
|
+
/** Before the label. Decorative: an icon-only button still needs `aria-label`. */
|
|
8
|
+
readonly icon?: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
export declare const Button: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
11
|
+
export interface SegmentedOption<T extends string> {
|
|
12
|
+
readonly value: T;
|
|
13
|
+
readonly label: string;
|
|
14
|
+
readonly icon?: ReactNode;
|
|
15
|
+
readonly disabled?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface SegmentedControlProps<T extends string> {
|
|
18
|
+
/** Two to five options, all worth seeing at once. More than that wants a pop-up menu. */
|
|
19
|
+
readonly options: readonly SegmentedOption<T>[];
|
|
20
|
+
readonly value: T;
|
|
21
|
+
readonly onChange: (value: T) => void;
|
|
22
|
+
/** The accessible name for the group, e.g. "Filter rides by bike". */
|
|
23
|
+
readonly label: string;
|
|
24
|
+
readonly size?: 'regular' | 'small';
|
|
25
|
+
readonly disabled?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare function SegmentedControl<T extends string>({ options, value, onChange, label, size, disabled }: SegmentedControlProps<T>): import("react").JSX.Element;
|
|
28
|
+
export interface ToggleProps {
|
|
29
|
+
readonly checked: boolean;
|
|
30
|
+
readonly onChange: (checked: boolean) => void;
|
|
31
|
+
/** Shown beside the switch. Use `label` alone, or `aria-label` when the context already names it. */
|
|
32
|
+
readonly label?: ReactNode;
|
|
33
|
+
readonly description?: ReactNode;
|
|
34
|
+
readonly disabled?: boolean;
|
|
35
|
+
readonly 'aria-label'?: string;
|
|
36
|
+
}
|
|
37
|
+
/** For a setting that takes effect immediately. One that waits for Save is a checkbox. */
|
|
38
|
+
export declare function Toggle({ checked, onChange, label, description, disabled, 'aria-label': ariaLabel }: ToggleProps): import("react").JSX.Element;
|
|
39
|
+
export interface CheckboxProps {
|
|
40
|
+
readonly checked: boolean;
|
|
41
|
+
/** Some of the things this stands for are checked, and some are not. */
|
|
42
|
+
readonly indeterminate?: boolean;
|
|
43
|
+
readonly onChange: (checked: boolean) => void;
|
|
44
|
+
readonly label?: ReactNode;
|
|
45
|
+
readonly description?: ReactNode;
|
|
46
|
+
readonly disabled?: boolean;
|
|
47
|
+
readonly 'aria-label'?: string;
|
|
48
|
+
}
|
|
49
|
+
/** For a setting that waits for Save. One that applies at once is a Toggle. */
|
|
50
|
+
export declare function Checkbox({ checked, indeterminate, onChange, label, description, disabled, 'aria-label': ariaLabel }: CheckboxProps): import("react").JSX.Element;
|
|
51
|
+
export interface RadioOption<T extends string> {
|
|
52
|
+
readonly value: T;
|
|
53
|
+
readonly label: string;
|
|
54
|
+
readonly description?: string;
|
|
55
|
+
readonly disabled?: boolean;
|
|
56
|
+
}
|
|
57
|
+
export interface RadioGroupProps<T extends string> {
|
|
58
|
+
readonly options: readonly RadioOption<T>[];
|
|
59
|
+
readonly value: T | null;
|
|
60
|
+
readonly onChange: (value: T) => void;
|
|
61
|
+
readonly label: string;
|
|
62
|
+
readonly labelHidden?: boolean;
|
|
63
|
+
readonly disabled?: boolean;
|
|
64
|
+
}
|
|
65
|
+
/** Mutually exclusive choices that each need a line of explanation. */
|
|
66
|
+
export declare function RadioGroup<T extends string>({ options, value, onChange, label, labelHidden, disabled }: RadioGroupProps<T>): import("react").JSX.Element;
|
|
67
|
+
export interface ChoiceOption<T extends string> {
|
|
68
|
+
readonly value: T;
|
|
69
|
+
readonly label: string;
|
|
70
|
+
readonly description?: ReactNode;
|
|
71
|
+
/** Decorative, beside the label. */
|
|
72
|
+
readonly icon?: ReactNode;
|
|
73
|
+
/** A short aside after the label: a size, "needs a key". */
|
|
74
|
+
readonly tag?: ReactNode;
|
|
75
|
+
readonly disabled?: boolean;
|
|
76
|
+
}
|
|
77
|
+
export interface ChoiceGroupProps<T extends string> {
|
|
78
|
+
readonly options: readonly ChoiceOption<T>[];
|
|
79
|
+
readonly value: T | null;
|
|
80
|
+
readonly onChange: (value: T) => void;
|
|
81
|
+
readonly label: string;
|
|
82
|
+
readonly labelHidden?: boolean;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The few choices a setup step turns on, as cards: bigger than a radio, smaller than a page.
|
|
86
|
+
* Native radios underneath, so the group is one tab stop and the arrow keys move the choice.
|
|
87
|
+
*/
|
|
88
|
+
export declare function ChoiceGroup<T extends string>({ options, value, onChange, label, labelHidden }: ChoiceGroupProps<T>): import("react").JSX.Element;
|
|
89
|
+
export interface SliderProps {
|
|
90
|
+
readonly value: number;
|
|
91
|
+
readonly onChange: (value: number) => void;
|
|
92
|
+
readonly label: string;
|
|
93
|
+
readonly min?: number;
|
|
94
|
+
readonly max?: number;
|
|
95
|
+
readonly step?: number;
|
|
96
|
+
readonly disabled?: boolean;
|
|
97
|
+
/** How the value reads to a person: `v => \`${v} psi\``. Shown beside the label and announced. */
|
|
98
|
+
readonly format?: (value: number) => string;
|
|
99
|
+
}
|
|
100
|
+
export declare function Slider({ value, onChange, label, min, max, step, disabled, format }: SliderProps): import("react").JSX.Element;
|
|
101
|
+
export interface TextFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'className' | 'id'> {
|
|
102
|
+
/** Every field has a label. A placeholder is an example, never the label. */
|
|
103
|
+
readonly label: string;
|
|
104
|
+
/** Keep the label for assistive technology, hide it on screen — for a search field in a toolbar. */
|
|
105
|
+
readonly labelHidden?: boolean;
|
|
106
|
+
readonly help?: ReactNode;
|
|
107
|
+
/** What went wrong and how to fix it. Its presence marks the field invalid. */
|
|
108
|
+
readonly error?: ReactNode;
|
|
109
|
+
}
|
|
110
|
+
export declare const TextField: import("react").ForwardRefExoticComponent<TextFieldProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
111
|
+
//# sourceMappingURL=controls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controls.d.ts","sourceRoot":"","sources":["../../src/react/controls.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useEffect, useId, useRef } from 'react';
|
|
3
|
+
export const Button = forwardRef(function Button({ intent = 'normal', size = 'regular', icon, children, type = 'button', className, ...rest }, ref) {
|
|
4
|
+
return (_jsxs("button", { ref: ref, type: type, className: ['desk-button', className].filter(Boolean).join(' '), "data-intent": intent, "data-size": size, "data-icon-only": !children || undefined, ...rest, children: [icon && _jsx("span", { className: "desk-button-icon", "aria-hidden": "true", children: icon }), children] }));
|
|
5
|
+
});
|
|
6
|
+
export function SegmentedControl({ options, value, onChange, label, size = 'regular', disabled }) {
|
|
7
|
+
const group = useRef(null);
|
|
8
|
+
const move = (event) => {
|
|
9
|
+
const keys = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 };
|
|
10
|
+
const delta = keys[event.key];
|
|
11
|
+
const usable = options.filter(o => !o.disabled);
|
|
12
|
+
if (!delta || !usable.length)
|
|
13
|
+
return;
|
|
14
|
+
event.preventDefault();
|
|
15
|
+
const at = usable.findIndex(o => o.value === value);
|
|
16
|
+
const next = usable[(at + delta + usable.length) % usable.length];
|
|
17
|
+
if (!next)
|
|
18
|
+
return;
|
|
19
|
+
onChange(next.value);
|
|
20
|
+
// Selection follows focus here, as it does in a segmented control on any platform.
|
|
21
|
+
group.current?.querySelector(`[data-value="${CSS.escape(next.value)}"]`)?.focus();
|
|
22
|
+
};
|
|
23
|
+
return (_jsx("div", { ref: group, role: "radiogroup", "aria-label": label, className: "desk-segmented", "data-size": size, onKeyDown: move, children: options.map(option => {
|
|
24
|
+
const selected = option.value === value;
|
|
25
|
+
return (_jsxs("button", { type: "button", role: "radio", "aria-checked": selected, "data-value": option.value, className: "desk-segment", tabIndex: selected ? 0 : -1, disabled: disabled || option.disabled, onClick: () => onChange(option.value), children: [option.icon && _jsx("span", { className: "desk-segment-icon", "aria-hidden": "true", children: option.icon }), option.label] }, option.value));
|
|
26
|
+
}) }));
|
|
27
|
+
}
|
|
28
|
+
/** For a setting that takes effect immediately. One that waits for Save is a checkbox. */
|
|
29
|
+
export function Toggle({ checked, onChange, label, description, disabled, 'aria-label': ariaLabel }) {
|
|
30
|
+
const id = useId();
|
|
31
|
+
const descriptionId = description ? `${id}-description` : undefined;
|
|
32
|
+
const control = (_jsx("button", { type: "button", role: "switch", id: id, "aria-checked": checked, "aria-label": ariaLabel, "aria-describedby": descriptionId, className: "desk-toggle", disabled: disabled, onClick: () => onChange(!checked), children: _jsx("span", { className: "desk-toggle-knob", "aria-hidden": "true" }) }));
|
|
33
|
+
if (!label && !description)
|
|
34
|
+
return control;
|
|
35
|
+
return (_jsxs("div", { className: "desk-toggle-row", children: [_jsxs("span", { className: "desk-toggle-text", children: [label && _jsx("label", { htmlFor: id, className: "desk-label", children: label }), description && _jsx("span", { id: descriptionId, className: "desk-help", children: description })] }), control] }));
|
|
36
|
+
}
|
|
37
|
+
/** For a setting that waits for Save. One that applies at once is a Toggle. */
|
|
38
|
+
export function Checkbox({ checked, indeterminate, onChange, label, description, disabled, 'aria-label': ariaLabel }) {
|
|
39
|
+
const id = useId();
|
|
40
|
+
const descriptionId = description ? `${id}-description` : undefined;
|
|
41
|
+
const box = useRef(null);
|
|
42
|
+
// Indeterminate is a property, not an attribute: it cannot be set in markup.
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (box.current)
|
|
45
|
+
box.current.indeterminate = indeterminate ?? false;
|
|
46
|
+
}, [indeterminate]);
|
|
47
|
+
const input = (_jsx("input", { ref: box, id: id, type: "checkbox", className: "desk-checkbox", checked: checked, disabled: disabled, "aria-label": ariaLabel, "aria-describedby": descriptionId, onChange: event => onChange(event.target.checked) }));
|
|
48
|
+
if (!label && !description)
|
|
49
|
+
return input;
|
|
50
|
+
return (_jsxs("div", { className: "desk-check-row", children: [input, _jsxs("span", { className: "desk-check-text", children: [label && _jsx("label", { htmlFor: id, className: "desk-check-label", children: label }), description && _jsx("span", { id: descriptionId, className: "desk-help", children: description })] })] }));
|
|
51
|
+
}
|
|
52
|
+
/** Mutually exclusive choices that each need a line of explanation. */
|
|
53
|
+
export function RadioGroup({ options, value, onChange, label, labelHidden, disabled }) {
|
|
54
|
+
const name = useId();
|
|
55
|
+
return (_jsxs("fieldset", { className: "desk-radiogroup", disabled: disabled, children: [_jsx("legend", { className: "desk-label", "data-hidden": labelHidden || undefined, children: label }), options.map(option => {
|
|
56
|
+
const id = `${name}-${option.value}`;
|
|
57
|
+
return (_jsxs("div", { className: "desk-check-row", children: [_jsx("input", { id: id, type: "radio", name: name, className: "desk-radio", value: option.value, checked: option.value === value, disabled: option.disabled, "aria-describedby": option.description ? `${id}-description` : undefined, onChange: () => onChange(option.value) }), _jsxs("span", { className: "desk-check-text", children: [_jsx("label", { htmlFor: id, className: "desk-check-label", children: option.label }), option.description && _jsx("span", { id: `${id}-description`, className: "desk-help", children: option.description })] })] }, option.value));
|
|
58
|
+
})] }));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The few choices a setup step turns on, as cards: bigger than a radio, smaller than a page.
|
|
62
|
+
* Native radios underneath, so the group is one tab stop and the arrow keys move the choice.
|
|
63
|
+
*/
|
|
64
|
+
export function ChoiceGroup({ options, value, onChange, label, labelHidden }) {
|
|
65
|
+
const name = useId();
|
|
66
|
+
return (_jsxs("fieldset", { className: "desk-choices", children: [_jsx("legend", { className: "desk-label", "data-hidden": labelHidden || undefined, children: label }), options.map(option => {
|
|
67
|
+
const id = `${name}-${option.value}`;
|
|
68
|
+
const chosen = option.value === value;
|
|
69
|
+
return (_jsxs("label", { htmlFor: id, className: "desk-choice", "data-chosen": chosen || undefined, "data-disabled": option.disabled || undefined, children: [_jsx("input", { id: id, type: "radio", name: name, className: "desk-choice-input", value: option.value, checked: chosen, disabled: option.disabled, "aria-describedby": option.description ? `${id}-description` : undefined, onChange: () => onChange(option.value) }), _jsx("span", { className: "desk-choice-mark", "aria-hidden": "true" }), option.icon && _jsx("span", { className: "desk-choice-icon", "aria-hidden": "true", children: option.icon }), _jsxs("span", { className: "desk-choice-text", children: [_jsxs("span", { className: "desk-choice-label", children: [option.label, option.tag && _jsx("span", { className: "desk-choice-tag", children: option.tag })] }), option.description && _jsx("span", { id: `${id}-description`, className: "desk-help", children: option.description })] })] }, option.value));
|
|
70
|
+
})] }));
|
|
71
|
+
}
|
|
72
|
+
export function Slider({ value, onChange, label, min = 0, max = 100, step = 1, disabled, format }) {
|
|
73
|
+
const id = useId();
|
|
74
|
+
const shown = format ? format(value) : String(value);
|
|
75
|
+
return (_jsxs("div", { className: "desk-slider", children: [_jsxs("div", { className: "desk-slider-head", children: [_jsx("label", { htmlFor: id, className: "desk-label", children: label }), _jsx("output", { htmlFor: id, className: "desk-slider-value", children: shown })] }), _jsx("input", { id: id, type: "range", className: "desk-slider-track", min: min, max: max, step: step, value: value, disabled: disabled, "aria-valuetext": format ? shown : undefined, onChange: event => onChange(Number(event.target.value)) })] }));
|
|
76
|
+
}
|
|
77
|
+
export const TextField = forwardRef(function TextField({ label, labelHidden, help, error, type = 'text', ...rest }, ref) {
|
|
78
|
+
const id = useId();
|
|
79
|
+
const helpId = help ? `${id}-help` : undefined;
|
|
80
|
+
const errorId = error ? `${id}-error` : undefined;
|
|
81
|
+
return (_jsxs("div", { className: "desk-field", "data-invalid": error ? true : undefined, children: [_jsx("label", { htmlFor: id, className: "desk-label", "data-hidden": labelHidden || undefined, children: label }), _jsx("input", { ref: ref, id: id, type: type, className: "desk-input", "aria-invalid": error ? true : undefined, "aria-describedby": [errorId, helpId].filter(Boolean).join(' ') || undefined, ...rest }), error && _jsx("span", { id: errorId, className: "desk-error", children: error }), help && !error && _jsx("span", { id: helpId, className: "desk-help", children: help })] }));
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=controls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controls.js","sourceRoot":"","sources":["../../src/react/controls.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface Message {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
/** Matches `me` on the thread for your own messages. Anything else is somebody else. */
|
|
5
|
+
readonly from: string;
|
|
6
|
+
/** Shown above the first message of a run. Falls back to `from`. */
|
|
7
|
+
readonly authorName?: string;
|
|
8
|
+
readonly body: ReactNode;
|
|
9
|
+
readonly at?: Date | string;
|
|
10
|
+
/** `sending` dims it, `failed` offers Retry. */
|
|
11
|
+
readonly state?: 'sending' | 'sent' | 'failed';
|
|
12
|
+
}
|
|
13
|
+
export interface ThreadProps {
|
|
14
|
+
readonly messages: readonly Message[];
|
|
15
|
+
/** Which `from` is you. Default `me`. */
|
|
16
|
+
readonly me?: string;
|
|
17
|
+
/** Shown while the other side is composing: `true` for dots, or your own node. */
|
|
18
|
+
readonly typing?: boolean | ReactNode;
|
|
19
|
+
readonly onRetry?: (id: string) => void;
|
|
20
|
+
readonly empty?: ReactNode;
|
|
21
|
+
readonly label?: string;
|
|
22
|
+
readonly className?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function Thread({ messages, me, typing, onRetry, empty, label, className }: ThreadProps): import("react").JSX.Element;
|
|
25
|
+
export interface ComposerProps {
|
|
26
|
+
readonly value: string;
|
|
27
|
+
readonly onChange: (value: string) => void;
|
|
28
|
+
/** Called on Enter or Send, with the trimmed text. Clearing the box is yours to do. */
|
|
29
|
+
readonly onSubmit: (value: string) => void;
|
|
30
|
+
readonly placeholder?: string;
|
|
31
|
+
readonly label?: string;
|
|
32
|
+
readonly disabled?: boolean;
|
|
33
|
+
/** An answer is arriving: Send becomes Stop when `onStop` is given. */
|
|
34
|
+
readonly busy?: boolean;
|
|
35
|
+
readonly onStop?: () => void;
|
|
36
|
+
/** Beside the send button — attachments, a model picker. */
|
|
37
|
+
readonly accessory?: ReactNode;
|
|
38
|
+
readonly maxRows?: number;
|
|
39
|
+
}
|
|
40
|
+
export declare function Composer({ value, onChange, onSubmit, placeholder, label, disabled, busy, onStop, accessory, maxRows, }: ComposerProps): import("react").JSX.Element;
|
|
41
|
+
//# sourceMappingURL=conversation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/react/conversation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AASrD,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,wFAAwF;IACxF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,oEAAoE;IACpE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;IAC3B,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAA;CAC/C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAA;IACrC,yCAAyC;IACzC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;IACpB,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B;AAOD,wBAAgB,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAS,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAsB,EAAE,SAAS,EAAE,EAAE,WAAW,+BAyFrH;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAA;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,WAA8B,EAC9B,KAAiB,EACjB,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,SAAS,EACT,OAAW,GACZ,EAAE,aAAa,+BAkDf"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
3
|
+
import { Button } from './controls.js';
|
|
4
|
+
const time = (at) => at === undefined ? null
|
|
5
|
+
: typeof at === 'string' ? at
|
|
6
|
+
: at.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' });
|
|
7
|
+
export function Thread({ messages, me = 'me', typing, onRetry, empty, label = 'Conversation', className }) {
|
|
8
|
+
const scroller = useRef(null);
|
|
9
|
+
const atBottom = useRef(true);
|
|
10
|
+
const [missed, setMissed] = useState(0);
|
|
11
|
+
const toBottom = useCallback((behavior = 'auto') => {
|
|
12
|
+
const element = scroller.current;
|
|
13
|
+
if (!element)
|
|
14
|
+
return;
|
|
15
|
+
element.scrollTo({ top: element.scrollHeight, behavior });
|
|
16
|
+
atBottom.current = true;
|
|
17
|
+
setMissed(0);
|
|
18
|
+
}, []);
|
|
19
|
+
// Follow the conversation only while the reader is at the end of it: scrolling
|
|
20
|
+
// back to read something must not be yanked away by the next message.
|
|
21
|
+
useLayoutEffect(() => {
|
|
22
|
+
if (atBottom.current)
|
|
23
|
+
toBottom();
|
|
24
|
+
else
|
|
25
|
+
setMissed(n => n + 1);
|
|
26
|
+
}, [messages.length, toBottom]);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
toBottom();
|
|
29
|
+
}, [toBottom]);
|
|
30
|
+
const onScroll = () => {
|
|
31
|
+
const element = scroller.current;
|
|
32
|
+
if (!element)
|
|
33
|
+
return;
|
|
34
|
+
atBottom.current = element.scrollHeight - element.scrollTop - element.clientHeight < 32;
|
|
35
|
+
if (atBottom.current)
|
|
36
|
+
setMissed(0);
|
|
37
|
+
};
|
|
38
|
+
let previous = null;
|
|
39
|
+
return (_jsxs("div", { className: ['desk-thread', className].filter(Boolean).join(' '), children: [_jsxs("div", { ref: scroller, className: "desk-thread-scroll", role: "log", "aria-label": label, "aria-live": "polite", onScroll: onScroll, children: [messages.length === 0 && !typing && empty, messages.map(message => {
|
|
40
|
+
const mine = message.from === me;
|
|
41
|
+
const runStart = message.from !== previous;
|
|
42
|
+
previous = message.from;
|
|
43
|
+
const stamp = time(message.at);
|
|
44
|
+
return (_jsxs("article", { className: "desk-message", "data-mine": mine || undefined, "data-state": message.state ?? 'sent', "data-run-start": runStart || undefined, children: [runStart && !mine && _jsx("span", { className: "desk-message-author", children: message.authorName ?? message.from }), _jsx("div", { className: "desk-bubble", children: message.body }), _jsxs("span", { className: "desk-message-foot", children: [stamp && _jsx("time", { children: stamp }), message.state === 'sending' && _jsx("span", { children: "Sending\u2026" }), message.state === 'failed' && (_jsxs(_Fragment, { children: [_jsx("span", { className: "desk-message-failed", children: "Not sent" }), onRetry && (_jsx(Button, { size: "small", intent: "quiet", onClick: () => onRetry(message.id), children: "Retry" }))] }))] })] }, message.id));
|
|
45
|
+
}), typing && (_jsx("div", { className: "desk-typing", "aria-label": "Typing", children: typing === true ? (_jsxs("span", { className: "desk-typing-dots", "aria-hidden": "true", children: [_jsx("i", {}), _jsx("i", {}), _jsx("i", {})] })) : (typing) }))] }), missed > 0 && (_jsxs(Button, { className: "desk-thread-jump", size: "small", onClick: () => toBottom('smooth'), children: [missed === 1 ? '1 new message' : `${missed} new messages`, " \u2193"] }))] }));
|
|
46
|
+
}
|
|
47
|
+
export function Composer({ value, onChange, onSubmit, placeholder = 'Say something…', label = 'Message', disabled, busy, onStop, accessory, maxRows = 8, }) {
|
|
48
|
+
const field = useRef(null);
|
|
49
|
+
// Grow with the text, up to maxRows, then scroll.
|
|
50
|
+
useLayoutEffect(() => {
|
|
51
|
+
const element = field.current;
|
|
52
|
+
if (!element)
|
|
53
|
+
return;
|
|
54
|
+
element.style.height = 'auto';
|
|
55
|
+
const line = parseFloat(getComputedStyle(element).lineHeight || '20') || 20;
|
|
56
|
+
element.style.height = `${Math.min(element.scrollHeight, line * maxRows)}px`;
|
|
57
|
+
}, [value, maxRows]);
|
|
58
|
+
const submit = () => {
|
|
59
|
+
const text = value.trim();
|
|
60
|
+
if (!text || disabled)
|
|
61
|
+
return;
|
|
62
|
+
onSubmit(text);
|
|
63
|
+
};
|
|
64
|
+
const onKeyDown = (event) => {
|
|
65
|
+
// Enter sends; Shift+Enter is a new line. IME composition must never send.
|
|
66
|
+
if (event.key !== 'Enter' || event.shiftKey || event.nativeEvent.isComposing)
|
|
67
|
+
return;
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
submit();
|
|
70
|
+
};
|
|
71
|
+
return (_jsxs("div", { className: "desk-composer", "data-disabled": disabled || undefined, children: [_jsx("textarea", { ref: field, className: "desk-composer-field", "aria-label": label, rows: 1, value: value, placeholder: placeholder, disabled: disabled, onChange: event => onChange(event.target.value), onKeyDown: onKeyDown }), accessory, busy && onStop ? (_jsx(Button, { "aria-label": "Stop", onClick: onStop, children: "Stop" })) : (_jsx(Button, { intent: "default", "aria-label": "Send", disabled: disabled || !value.trim(), onClick: submit, children: "Send" }))] }));
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=conversation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.js","sourceRoot":"","sources":["../../src/react/conversation.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { PointerEvent as ReactPointerEvent, ReactNode } from 'react';
|
|
2
|
+
import type { Accepts, Drag } from './dragContext.js';
|
|
3
|
+
export { DragProvider } from './dragContext.js';
|
|
4
|
+
export type { Accepts, Drag } from './dragContext.js';
|
|
5
|
+
/** What is being dragged right now, for a window that wants to show it is ready. */
|
|
6
|
+
export declare const useDragging: () => Drag | null;
|
|
7
|
+
export interface DraggableOptions<T> {
|
|
8
|
+
readonly type: string;
|
|
9
|
+
readonly payload: T;
|
|
10
|
+
/** What follows the pointer. Defaults to the type. */
|
|
11
|
+
readonly preview?: ReactNode;
|
|
12
|
+
readonly disabled?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface DraggableResult {
|
|
15
|
+
readonly dragProps: {
|
|
16
|
+
readonly onPointerDown: (event: ReactPointerEvent<HTMLElement>) => void;
|
|
17
|
+
readonly 'data-desk-draggable': true;
|
|
18
|
+
};
|
|
19
|
+
readonly dragging: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface DragSourceOptions {
|
|
22
|
+
readonly type: string;
|
|
23
|
+
readonly disabled?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface DragSource<T> {
|
|
26
|
+
/** Props for one item in a list: `rowProps={row => source.dragProps(row, row.name)}`. */
|
|
27
|
+
readonly dragProps: (payload: T, preview?: ReactNode) => DraggableResult['dragProps'];
|
|
28
|
+
readonly dragging: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* One source for a list of things: a hook cannot be called per row, so the row
|
|
32
|
+
* says what it is as it is picked up.
|
|
33
|
+
*/
|
|
34
|
+
export declare function useDragSource<T>({ type, disabled }: DragSourceOptions): DragSource<T>;
|
|
35
|
+
/** Makes one thing picked up and carried. Spread `dragProps` on the element. */
|
|
36
|
+
export declare function useDraggable<T>({ type, payload, preview, disabled }: DraggableOptions<T>): DraggableResult;
|
|
37
|
+
export interface DropTargetOptions<T> {
|
|
38
|
+
readonly accepts: Accepts;
|
|
39
|
+
readonly onDrop: (drag: Drag<T>) => void;
|
|
40
|
+
readonly disabled?: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface DropTargetResult {
|
|
43
|
+
readonly dropProps: Record<string, string | undefined>;
|
|
44
|
+
/** Something acceptable is being carried over this target right now. */
|
|
45
|
+
readonly over: boolean;
|
|
46
|
+
/** Something acceptable is being carried somewhere: show where it could go. */
|
|
47
|
+
readonly ready: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** Makes something a place to drop. Spread `dropProps` on the element. */
|
|
50
|
+
export declare function useDropTarget<T>({ accepts, onDrop, disabled }: DropTargetOptions<T>): DropTargetResult;
|
|
51
|
+
//# sourceMappingURL=dnd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dnd.d.ts","sourceRoot":"","sources":["../../src/react/dnd.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,IAAI,iBAAiB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAIzE,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAErD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAkBrD,oFAAoF;AACpF,eAAO,MAAM,WAAW,QAAO,IAAI,GAAG,IAAqC,CAAA;AAE3E,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,sDAAsD;IACtD,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAA;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE;QAAE,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;QAAC,QAAQ,CAAC,qBAAqB,EAAE,IAAI,CAAA;KAAE,CAAA;IACrI,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,yFAAyF;IACzF,QAAQ,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,SAAS,KAAK,eAAe,CAAC,WAAW,CAAC,CAAA;IACrF,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAC3B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,iBAAiB,GAAG,UAAU,CAAC,CAAC,CAAC,CA+ErF;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,eAAe,CAG1G;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IACtD,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,gBAAgB,CA6BtG"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { useCallback, useContext, useEffect, useId, useRef, useState } from 'react';
|
|
2
|
+
import { WINDOW_ATTRIBUTE } from '../core/commands.js';
|
|
3
|
+
import { useDesk, useWindowId } from './context.js';
|
|
4
|
+
import { accepted, DragContext, TARGET_ATTRIBUTE } from './dragContext.js';
|
|
5
|
+
export { DragProvider } from './dragContext.js';
|
|
6
|
+
/*
|
|
7
|
+
* Dragging something from one window to another — a ride onto the map, a
|
|
8
|
+
* document into a conversation. Built on Pointer Events rather than HTML5
|
|
9
|
+
* drag-and-drop: the same code then works for touch and pen, the preview is
|
|
10
|
+
* ours to draw, and a drag can be cancelled with Escape.
|
|
11
|
+
*
|
|
12
|
+
* What is dragged is a `type` and a `payload`. A target says which types it
|
|
13
|
+
* takes; nothing else about the two windows is shared.
|
|
14
|
+
*/
|
|
15
|
+
const useDrag = () => {
|
|
16
|
+
const context = useContext(DragContext);
|
|
17
|
+
if (!context)
|
|
18
|
+
throw new Error('Dragging needs a <DeskProvider> (or a <DragProvider>) above it');
|
|
19
|
+
return context;
|
|
20
|
+
};
|
|
21
|
+
/** What is being dragged right now, for a window that wants to show it is ready. */
|
|
22
|
+
export const useDragging = () => useDrag().state?.drag ?? null;
|
|
23
|
+
/**
|
|
24
|
+
* One source for a list of things: a hook cannot be called per row, so the row
|
|
25
|
+
* says what it is as it is picked up.
|
|
26
|
+
*/
|
|
27
|
+
export function useDragSource({ type, disabled }) {
|
|
28
|
+
const { targets, setState } = useDrag();
|
|
29
|
+
const from = useWindowId();
|
|
30
|
+
const desk = useDesk();
|
|
31
|
+
const [dragging, setDragging] = useState(false);
|
|
32
|
+
const start = useCallback((payload, preview) => (event) => {
|
|
33
|
+
if (disabled || event.button !== 0)
|
|
34
|
+
return;
|
|
35
|
+
const startX = event.clientX;
|
|
36
|
+
const startY = event.clientY;
|
|
37
|
+
const handle = event.currentTarget;
|
|
38
|
+
let started = false;
|
|
39
|
+
const findTarget = (x, y) => {
|
|
40
|
+
const element = document.elementFromPoint(x, y)?.closest(`[${TARGET_ATTRIBUTE}]`);
|
|
41
|
+
const key = element?.getAttribute(TARGET_ATTRIBUTE) ?? null;
|
|
42
|
+
const target = key ? targets.get(key) : undefined;
|
|
43
|
+
return target && accepted(target.accepts, type) ? key : null;
|
|
44
|
+
};
|
|
45
|
+
const onMove = (move) => {
|
|
46
|
+
if (!started && Math.hypot(move.clientX - startX, move.clientY - startY) < 5)
|
|
47
|
+
return;
|
|
48
|
+
if (!started) {
|
|
49
|
+
started = true;
|
|
50
|
+
setDragging(true);
|
|
51
|
+
handle.setPointerCapture?.(move.pointerId);
|
|
52
|
+
}
|
|
53
|
+
move.preventDefault();
|
|
54
|
+
setState({
|
|
55
|
+
drag: { type, payload, from },
|
|
56
|
+
x: move.clientX,
|
|
57
|
+
y: move.clientY,
|
|
58
|
+
over: findTarget(move.clientX, move.clientY),
|
|
59
|
+
preview: preview ?? type,
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
const finish = (drop, released) => {
|
|
63
|
+
document.removeEventListener('pointermove', onMove);
|
|
64
|
+
document.removeEventListener('pointerup', onUp);
|
|
65
|
+
document.removeEventListener('keydown', onKey);
|
|
66
|
+
if (!started)
|
|
67
|
+
return;
|
|
68
|
+
setDragging(false);
|
|
69
|
+
// Pointer capture sends the release back to where the drag began, and the browser then
|
|
70
|
+
// counts the whole gesture as a click on it. A drag is never also a click.
|
|
71
|
+
const swallow = (click) => {
|
|
72
|
+
click.stopPropagation();
|
|
73
|
+
click.preventDefault();
|
|
74
|
+
};
|
|
75
|
+
globalThis.addEventListener('click', swallow, { capture: true, once: true });
|
|
76
|
+
setTimeout(() => globalThis.removeEventListener('click', swallow, { capture: true }), 0);
|
|
77
|
+
const key = drop && released ? findTarget(released.clientX, released.clientY) : null;
|
|
78
|
+
const target = key ? targets.get(key) : undefined;
|
|
79
|
+
setState(null);
|
|
80
|
+
if (!target)
|
|
81
|
+
return;
|
|
82
|
+
target.onDrop({ type, payload, from });
|
|
83
|
+
// The window that took it comes forward: that is where the work moved to.
|
|
84
|
+
const receiving = document.querySelector(`[${TARGET_ATTRIBUTE}="${key}"]`)?.closest(`[${WINDOW_ATTRIBUTE}]`);
|
|
85
|
+
const windowId = receiving?.getAttribute(WINDOW_ATTRIBUTE);
|
|
86
|
+
if (windowId)
|
|
87
|
+
desk.focus(windowId);
|
|
88
|
+
};
|
|
89
|
+
const onUp = (up) => finish(true, up);
|
|
90
|
+
const onKey = (key) => {
|
|
91
|
+
if (key.key === 'Escape')
|
|
92
|
+
finish(false);
|
|
93
|
+
};
|
|
94
|
+
document.addEventListener('pointermove', onMove);
|
|
95
|
+
document.addEventListener('pointerup', onUp);
|
|
96
|
+
document.addEventListener('keydown', onKey);
|
|
97
|
+
}, [disabled, type, from, setState, targets, desk]);
|
|
98
|
+
return {
|
|
99
|
+
dragProps: useCallback((payload, preview) => ({ onPointerDown: start(payload, preview), 'data-desk-draggable': true }), [start]),
|
|
100
|
+
dragging,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/** Makes one thing picked up and carried. Spread `dragProps` on the element. */
|
|
104
|
+
export function useDraggable({ type, payload, preview, disabled }) {
|
|
105
|
+
const source = useDragSource({ type, ...(disabled === undefined ? {} : { disabled }) });
|
|
106
|
+
return { dragProps: source.dragProps(payload, preview), dragging: source.dragging };
|
|
107
|
+
}
|
|
108
|
+
/** Makes something a place to drop. Spread `dropProps` on the element. */
|
|
109
|
+
export function useDropTarget({ accepts, onDrop, disabled }) {
|
|
110
|
+
const { state, targets } = useDrag();
|
|
111
|
+
const id = useId();
|
|
112
|
+
const latest = useRef({ accepts, onDrop });
|
|
113
|
+
latest.current = { accepts, onDrop };
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (disabled)
|
|
116
|
+
return;
|
|
117
|
+
targets.set(id, {
|
|
118
|
+
accepts: type => accepted(latest.current.accepts, type),
|
|
119
|
+
onDrop: drag => latest.current.onDrop(drag),
|
|
120
|
+
});
|
|
121
|
+
return () => {
|
|
122
|
+
targets.delete(id);
|
|
123
|
+
};
|
|
124
|
+
}, [id, targets, disabled]);
|
|
125
|
+
const ready = Boolean(state && !disabled && accepted(accepts, state.drag.type));
|
|
126
|
+
const over = ready && state?.over === id;
|
|
127
|
+
return {
|
|
128
|
+
// The marks the stylesheet looks for, so a target lights up without the app wiring it.
|
|
129
|
+
dropProps: {
|
|
130
|
+
[TARGET_ATTRIBUTE]: disabled ? undefined : id,
|
|
131
|
+
...(ready ? { 'data-ready': 'true' } : {}),
|
|
132
|
+
...(over ? { 'data-over': 'true' } : {}),
|
|
133
|
+
},
|
|
134
|
+
over,
|
|
135
|
+
ready,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=dnd.js.map
|