@venizia/ardor-react 0.4.8-0 → 0.4.8-10
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/README.md +8 -5
- package/dist/contexts/application.d.ts +4 -0
- package/dist/contexts/application.js +1 -0
- package/dist/hooks/element-target.d.ts +9 -0
- package/dist/hooks/element-target.js +7 -0
- package/dist/hooks/index.d.ts +6 -1
- package/dist/hooks/index.js +5 -1
- package/dist/hooks/redux/use-app-dispatch.d.ts +5 -1
- package/dist/hooks/redux/use-app-dispatch.js +5 -1
- package/dist/hooks/redux/use-app-selector.d.ts +1 -0
- package/dist/hooks/redux/use-app-selector.js +28 -2
- package/dist/hooks/resize-observation.d.ts +21 -0
- package/dist/hooks/resize-observation.js +83 -0
- package/dist/hooks/use-application-context.d.ts +2 -0
- package/dist/hooks/use-application-context.js +2 -0
- package/dist/hooks/use-artifact.d.ts +26 -0
- package/dist/hooks/use-artifact.js +24 -2
- package/dist/hooks/use-autosave.d.ts +8 -0
- package/dist/hooks/use-autosave.js +4 -0
- package/dist/hooks/use-before-unload.d.ts +3 -0
- package/dist/hooks/use-before-unload.js +1 -0
- package/dist/hooks/use-confirm.d.ts +7 -0
- package/dist/hooks/use-confirm.js +1 -0
- package/dist/hooks/use-copy-to-clipboard.d.ts +23 -1
- package/dist/hooks/use-copy-to-clipboard.js +39 -15
- package/dist/hooks/use-debounce.d.ts +6 -0
- package/dist/hooks/use-debounce.js +1 -0
- package/dist/hooks/use-document-visibility.d.ts +3 -0
- package/dist/hooks/use-document-visibility.js +10 -0
- package/dist/hooks/use-element-size.d.ts +16 -1
- package/dist/hooks/use-element-size.js +11 -20
- package/dist/hooks/use-injectable.d.ts +32 -1
- package/dist/hooks/use-injectable.js +17 -3
- package/dist/hooks/use-intersection.d.ts +45 -0
- package/dist/hooks/use-intersection.js +74 -0
- package/dist/hooks/use-media-query.d.ts +5 -0
- package/dist/hooks/use-media-query.js +14 -0
- package/dist/hooks/use-online-status.d.ts +3 -0
- package/dist/hooks/use-online-status.js +14 -0
- package/dist/hooks/use-resize-effect.d.ts +41 -0
- package/dist/hooks/use-resize-effect.js +44 -0
- package/dist/hooks/use-stored-state.d.ts +32 -2
- package/dist/hooks/use-stored-state.js +74 -11
- package/dist/hooks/use-window-dimensions.d.ts +1 -0
- package/dist/hooks/use-window-dimensions.js +1 -0
- package/package.json +16 -17
- package/dist/contexts/application.d.ts.map +0 -1
- package/dist/contexts/application.js.map +0 -1
- package/dist/contexts/index.d.ts.map +0 -1
- package/dist/contexts/index.js.map +0 -1
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js.map +0 -1
- package/dist/hooks/redux/index.d.ts.map +0 -1
- package/dist/hooks/redux/index.js.map +0 -1
- package/dist/hooks/redux/use-app-dispatch.d.ts.map +0 -1
- package/dist/hooks/redux/use-app-dispatch.js.map +0 -1
- package/dist/hooks/redux/use-app-selector.d.ts.map +0 -1
- package/dist/hooks/redux/use-app-selector.js.map +0 -1
- package/dist/hooks/use-application-context.d.ts.map +0 -1
- package/dist/hooks/use-application-context.js.map +0 -1
- package/dist/hooks/use-artifact.d.ts.map +0 -1
- package/dist/hooks/use-artifact.js.map +0 -1
- package/dist/hooks/use-autosave.d.ts.map +0 -1
- package/dist/hooks/use-autosave.js.map +0 -1
- package/dist/hooks/use-before-unload.d.ts.map +0 -1
- package/dist/hooks/use-before-unload.js.map +0 -1
- package/dist/hooks/use-confirm.d.ts.map +0 -1
- package/dist/hooks/use-confirm.js.map +0 -1
- package/dist/hooks/use-copy-to-clipboard.d.ts.map +0 -1
- package/dist/hooks/use-copy-to-clipboard.js.map +0 -1
- package/dist/hooks/use-debounce.d.ts.map +0 -1
- package/dist/hooks/use-debounce.js.map +0 -1
- package/dist/hooks/use-element-size.d.ts.map +0 -1
- package/dist/hooks/use-element-size.js.map +0 -1
- package/dist/hooks/use-injectable.d.ts.map +0 -1
- package/dist/hooks/use-injectable.js.map +0 -1
- package/dist/hooks/use-sizer.d.ts +0 -10
- package/dist/hooks/use-sizer.d.ts.map +0 -1
- package/dist/hooks/use-sizer.js +0 -23
- package/dist/hooks/use-sizer.js.map +0 -1
- package/dist/hooks/use-stored-state.d.ts.map +0 -1
- package/dist/hooks/use-stored-state.js.map +0 -1
- package/dist/hooks/use-window-dimensions.d.ts.map +0 -1
- package/dist/hooks/use-window-dimensions.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,9 +4,11 @@ The React bindings of [ARDOR](https://github.com/VENIZIA-AI/ardor): the applicat
|
|
|
4
4
|
hooks that resolve out of it, and the UI hooks. No react-admin.
|
|
5
5
|
|
|
6
6
|
```bash
|
|
7
|
-
bun add @venizia/ardor-react @venizia/ardor-kernel @venizia/ignis-inversion @venizia/ignis-filter @venizia/ignis-kernel @venizia/ignis-helpers reflect-metadata
|
|
7
|
+
bun add @venizia/ardor-react @venizia/ardor-kernel @venizia/ignis-inversion @venizia/ignis-filter @venizia/ignis-kernel @venizia/ignis-helpers reflect-metadata react react-redux
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
+
`@reduxjs/toolkit` is an optional peer: only the types of `DispatchHooks` use it.
|
|
11
|
+
|
|
10
12
|
Most applications install [`@venizia/ardor`](https://www.npmjs.com/package/@venizia/ardor) instead,
|
|
11
13
|
which re-exports this package.
|
|
12
14
|
|
|
@@ -15,15 +17,14 @@ which re-exports this package.
|
|
|
15
17
|
| Area | Exports |
|
|
16
18
|
|---|---|
|
|
17
19
|
| Context | `ApplicationContext`, `useApplicationContext`, `useApplicationLogger` |
|
|
18
|
-
| Injection | `useInjectable` - resolve a binding by key or by class |
|
|
20
|
+
| Injection | `useInjectable` - resolve a binding by key or by class; the artifact hooks `useApplicationContainer`, `useService`, `useProvider`, `useComponent`, `useConfiguration`, `useRepository` |
|
|
19
21
|
| Redux | `DispatchHooks.create`, `SelectorHooks.create` - factories that bind your `RootState` and `AppDispatch` (the old `createAppDispatch` and `createAppSelectors` are removed; `bun run ardor codemod namespace` moves your code) |
|
|
20
|
-
| UI hooks | `useDebounce`, `useAutosave`, `useConfirm`, `useBeforeUnload`, `useCopyToClipboard`, `
|
|
22
|
+
| UI hooks | `useDebounce`, `useAutosave`, `useConfirm`, `useBeforeUnload`, `useCopyToClipboard`, `useWindowDimensions`, `useElementSize`, `useResizeEffect`, `useIntersection`, `useStoredState`, `useMediaQuery`, `useDocumentVisibility`, `useOnlineStatus` |
|
|
21
23
|
| Types | `TUseInjectableKeys` |
|
|
22
24
|
|
|
23
25
|
## Typing your own binding keys
|
|
24
26
|
|
|
25
|
-
`IUseInjectableKeysOverrides` is declared here, so augment this package
|
|
26
|
-
into the module that declares it, never through a re-export:
|
|
27
|
+
`IUseInjectableKeysOverrides` is declared here, so augment this package when it is the one you depend on:
|
|
27
28
|
|
|
28
29
|
```typescript
|
|
29
30
|
declare module '@venizia/ardor-react' {
|
|
@@ -33,6 +34,8 @@ declare module '@venizia/ardor-react' {
|
|
|
33
34
|
}
|
|
34
35
|
```
|
|
35
36
|
|
|
37
|
+
With `@venizia/ardor`, write `declare module '@venizia/ardor'` instead.
|
|
38
|
+
|
|
36
39
|
## License
|
|
37
40
|
|
|
38
41
|
MIT - see [LICENSE.md](./LICENSE.md).
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Container } from '@venizia/ignis-inversion';
|
|
3
3
|
import type { ILogger } from '@venizia/ignis-helpers/core';
|
|
4
|
+
/** What `ApplicationContext` carries; both are `null` outside a provider. */
|
|
4
5
|
export interface IApplicationContextValue {
|
|
6
|
+
/** The application's container. */
|
|
5
7
|
container: Container | null;
|
|
8
|
+
/** The application's logger. */
|
|
6
9
|
logger: ILogger | null;
|
|
7
10
|
}
|
|
11
|
+
/** Read through `useApplicationContext` and `useApplicationLogger`; the default value holds `null`s. */
|
|
8
12
|
export declare const ApplicationContext: React.Context<IApplicationContextValue>;
|
|
9
13
|
//# sourceMappingURL=application.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
/** An element, a ref to one, or `null` for none yet. */
|
|
3
|
+
export type TElementTarget = React.RefObject<Element | null> | Element | null;
|
|
4
|
+
export declare class ElementTargets {
|
|
5
|
+
static resolve(opts: {
|
|
6
|
+
target: TElementTarget;
|
|
7
|
+
}): Element | null;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=element-target.d.ts.map
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export type { TElementTarget } from './element-target.js';
|
|
1
2
|
export * from './redux/index.js';
|
|
2
3
|
export * from './use-application-context.js';
|
|
3
4
|
export * from './use-artifact.js';
|
|
@@ -6,9 +7,13 @@ export * from './use-before-unload.js';
|
|
|
6
7
|
export * from './use-confirm.js';
|
|
7
8
|
export * from './use-copy-to-clipboard.js';
|
|
8
9
|
export * from './use-debounce.js';
|
|
10
|
+
export * from './use-document-visibility.js';
|
|
9
11
|
export * from './use-element-size.js';
|
|
10
12
|
export * from './use-injectable.js';
|
|
11
|
-
export * from './use-
|
|
13
|
+
export * from './use-intersection.js';
|
|
14
|
+
export * from './use-media-query.js';
|
|
15
|
+
export * from './use-online-status.js';
|
|
16
|
+
export * from './use-resize-effect.js';
|
|
12
17
|
export * from './use-stored-state.js';
|
|
13
18
|
export * from './use-window-dimensions.js';
|
|
14
19
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/hooks/index.js
CHANGED
|
@@ -6,9 +6,13 @@ export * from './use-before-unload.js';
|
|
|
6
6
|
export * from './use-confirm.js';
|
|
7
7
|
export * from './use-copy-to-clipboard.js';
|
|
8
8
|
export * from './use-debounce.js';
|
|
9
|
+
export * from './use-document-visibility.js';
|
|
9
10
|
export * from './use-element-size.js';
|
|
10
11
|
export * from './use-injectable.js';
|
|
11
|
-
export * from './use-
|
|
12
|
+
export * from './use-intersection.js';
|
|
13
|
+
export * from './use-media-query.js';
|
|
14
|
+
export * from './use-online-status.js';
|
|
15
|
+
export * from './use-resize-effect.js';
|
|
12
16
|
export * from './use-stored-state.js';
|
|
13
17
|
export * from './use-window-dimensions.js';
|
|
14
18
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { Dispatch, UnknownAction } from '@reduxjs/toolkit';
|
|
2
|
+
/** Builds the typed dispatch hooks of a store. */
|
|
2
3
|
export declare class DispatchHooks {
|
|
3
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* The typed dispatch hooks of a store: bind your `AppDispatch` once, and export what comes back.
|
|
6
|
+
* `useMultipleAppDispatch` returns a function that dispatches each action in order and skips falsy ones.
|
|
7
|
+
*/
|
|
4
8
|
static create<TDispatch extends Dispatch<UnknownAction> = Dispatch<UnknownAction>>(): {
|
|
5
9
|
useAppDispatch: import("react-redux").UseDispatch<TDispatch>;
|
|
6
10
|
useMultipleAppDispatch: () => (...actions: Parameters<TDispatch>[0][]) => void;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useDispatch } from 'react-redux';
|
|
3
|
+
/** Builds the typed dispatch hooks of a store. */
|
|
3
4
|
export class DispatchHooks {
|
|
4
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* The typed dispatch hooks of a store: bind your `AppDispatch` once, and export what comes back.
|
|
7
|
+
* `useMultipleAppDispatch` returns a function that dispatches each action in order and skips falsy ones.
|
|
8
|
+
*/
|
|
5
9
|
static create() {
|
|
6
10
|
const useAppDispatch = useDispatch.withTypes();
|
|
7
11
|
const useMultipleAppDispatch = () => {
|
|
@@ -1,5 +1,31 @@
|
|
|
1
|
-
import isEqual from 'lodash/isEqual.js';
|
|
2
1
|
import { shallowEqual, useSelector } from 'react-redux';
|
|
2
|
+
const isPlain = (value) => {
|
|
3
|
+
return (typeof value === 'object' &&
|
|
4
|
+
value !== null &&
|
|
5
|
+
(Array.isArray(value) || [Object.prototype, null].includes(Object.getPrototypeOf(value))));
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Equality by value for what a selector returns: primitives, plain objects, arrays and dates, none
|
|
9
|
+
* of them cyclic; `0` equals `-0`, and `NaN` equals `NaN`. Any other object (a `Map`, a class
|
|
10
|
+
* instance) is equal only to itself.
|
|
11
|
+
*/
|
|
12
|
+
const deepEqual = (a, b) => {
|
|
13
|
+
if (a === b || Object.is(a, b)) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (a instanceof Date && b instanceof Date) {
|
|
17
|
+
return Object.is(a.getTime(), b.getTime());
|
|
18
|
+
}
|
|
19
|
+
if (!isPlain(a) || !isPlain(b) || Array.isArray(a) !== Array.isArray(b)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const keys = Object.keys(a);
|
|
23
|
+
return (keys.length === Object.keys(b).length &&
|
|
24
|
+
keys.every((key) => {
|
|
25
|
+
return Object.hasOwn(b, key) && deepEqual(Reflect.get(a, key), Reflect.get(b, key));
|
|
26
|
+
}));
|
|
27
|
+
};
|
|
28
|
+
/** Builds the typed selector hooks of a Redux store. */
|
|
3
29
|
export class SelectorHooks {
|
|
4
30
|
/** The three typed selector hooks of a store: bind your `RootState` once, and export what comes back. */
|
|
5
31
|
static create() {
|
|
@@ -8,7 +34,7 @@ export class SelectorHooks {
|
|
|
8
34
|
return useAppSelector(selector, shallowEqual);
|
|
9
35
|
};
|
|
10
36
|
const useDeepEqualSelector = (selector) => {
|
|
11
|
-
return useAppSelector(selector,
|
|
37
|
+
return useAppSelector(selector, deepEqual);
|
|
12
38
|
};
|
|
13
39
|
return {
|
|
14
40
|
useAppSelector,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** The boxes `useElementSize` and `useResizeEffect` read. */
|
|
2
|
+
export type TResizeBox = Exclude<ResizeObserverBoxOptions, 'device-pixel-content-box'>;
|
|
3
|
+
export interface IResizeObservationOptions {
|
|
4
|
+
box: TResizeBox;
|
|
5
|
+
/** Also observe the direct children of each target, and again whenever they change. @default false */
|
|
6
|
+
children?: boolean;
|
|
7
|
+
/** Call `onFrame` inside the `ResizeObserver` callback, after layout and before paint, instead of on the next animation frame. */
|
|
8
|
+
beforePaint?: boolean;
|
|
9
|
+
/** Called with the latest entry of each element that resized: at most once per animation frame, or once per observer callback with `beforePaint`. */
|
|
10
|
+
onFrame: (entries: ResizeObserverEntry[]) => void;
|
|
11
|
+
}
|
|
12
|
+
export interface IResizeObservation {
|
|
13
|
+
/** Observes these elements and stops observing the ones no longer listed. */
|
|
14
|
+
sync: (targets: Element[]) => void;
|
|
15
|
+
stop: () => void;
|
|
16
|
+
}
|
|
17
|
+
export declare class ResizeObservation {
|
|
18
|
+
/** Does nothing without `ResizeObserver`. */
|
|
19
|
+
static observe(opts: IResizeObservationOptions): IResizeObservation;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=resize-observation.d.ts.map
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export class ResizeObservation {
|
|
2
|
+
/** Does nothing without `ResizeObserver`. */
|
|
3
|
+
static observe(opts) {
|
|
4
|
+
const { box, children = false, beforePaint = false, onFrame } = opts;
|
|
5
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
6
|
+
return { sync: () => { }, stop: () => { } };
|
|
7
|
+
}
|
|
8
|
+
const latest = new Map();
|
|
9
|
+
let watched = new Set();
|
|
10
|
+
let current = [];
|
|
11
|
+
let frame;
|
|
12
|
+
const flush = () => {
|
|
13
|
+
const batch = [...latest.values()];
|
|
14
|
+
latest.clear();
|
|
15
|
+
if (batch.length > 0) {
|
|
16
|
+
onFrame(batch);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
let observer;
|
|
20
|
+
const onEntries = (entries) => {
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
latest.set(entry.target, entry);
|
|
23
|
+
}
|
|
24
|
+
if (beforePaint) {
|
|
25
|
+
flush();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (frame !== undefined) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
frame = requestAnimationFrame(() => {
|
|
32
|
+
frame = undefined;
|
|
33
|
+
flush();
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
const update = (targets) => {
|
|
37
|
+
const wanted = new Set(children ? targets.flatMap((target) => [target, ...target.children]) : targets);
|
|
38
|
+
// No target and no observer yet: there is nothing to watch, so none is made.
|
|
39
|
+
if (!observer && wanted.size === 0) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
observer ??= new ResizeObserver(onEntries);
|
|
43
|
+
for (const element of watched) {
|
|
44
|
+
if (!wanted.has(element)) {
|
|
45
|
+
observer.unobserve(element);
|
|
46
|
+
latest.delete(element);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const element of wanted) {
|
|
50
|
+
if (!watched.has(element)) {
|
|
51
|
+
observer.observe(element, { box });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
watched = wanted;
|
|
55
|
+
};
|
|
56
|
+
let mutations;
|
|
57
|
+
return {
|
|
58
|
+
sync: (targets) => {
|
|
59
|
+
if (targets.length === current.length &&
|
|
60
|
+
targets.every((target, index) => target === current[index])) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
current = targets;
|
|
64
|
+
update(targets);
|
|
65
|
+
// A removed target stays registered on `mutations` until `stop`; its callback only updates the current targets.
|
|
66
|
+
if (children && targets.length > 0) {
|
|
67
|
+
mutations ??= new MutationObserver(() => update(current));
|
|
68
|
+
for (const target of targets) {
|
|
69
|
+
mutations.observe(target, { childList: true });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
stop: () => {
|
|
74
|
+
mutations?.disconnect();
|
|
75
|
+
observer?.disconnect();
|
|
76
|
+
if (frame !== undefined) {
|
|
77
|
+
cancelAnimationFrame(frame);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=resize-observation.js.map
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** The application's `Container`. Throws when there is no `ApplicationContext` provider above. */
|
|
1
2
|
export declare const useApplicationContext: () => import("@venizia/ignis-inversion").Container;
|
|
3
|
+
/** The application's logger. Throws when there is no `ApplicationContext` provider above. */
|
|
2
4
|
export declare const useApplicationLogger: () => import("@venizia/ignis-helpers/core").ILogger;
|
|
3
5
|
//# sourceMappingURL=use-application-context.d.ts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { getError } from '@venizia/ignis-inversion';
|
|
3
3
|
import { ApplicationContext } from '../contexts/application.js';
|
|
4
|
+
/** The application's `Container`. Throws when there is no `ApplicationContext` provider above. */
|
|
4
5
|
export const useApplicationContext = () => {
|
|
5
6
|
const context = React.useContext(ApplicationContext);
|
|
6
7
|
if (!context?.container) {
|
|
@@ -10,6 +11,7 @@ export const useApplicationContext = () => {
|
|
|
10
11
|
}
|
|
11
12
|
return context.container;
|
|
12
13
|
};
|
|
14
|
+
/** The application's logger. Throws when there is no `ApplicationContext` provider above. */
|
|
13
15
|
export const useApplicationLogger = () => {
|
|
14
16
|
const context = React.useContext(ApplicationContext);
|
|
15
17
|
if (!context?.logger) {
|
|
@@ -2,19 +2,45 @@ import { type Container, type TClass } from '@venizia/ignis-inversion';
|
|
|
2
2
|
import { type TUseInjectableKeys } from './use-injectable.js';
|
|
3
3
|
/** By class: the return type is inferred from the target. */
|
|
4
4
|
export interface IUseArtifactByTarget<TInstance> {
|
|
5
|
+
/** The container to resolve from. @default The application's container (`ApplicationContext`) */
|
|
5
6
|
container?: Container;
|
|
7
|
+
/** The class to resolve; its recorded binding key is used. */
|
|
6
8
|
target: TClass<TInstance>;
|
|
7
9
|
key?: never;
|
|
8
10
|
}
|
|
9
11
|
/** By key: the caller supplies the type. */
|
|
10
12
|
export interface IUseArtifactByKey {
|
|
13
|
+
/** The container to resolve from. @default The application's container (`ApplicationContext`) */
|
|
11
14
|
container?: Container;
|
|
15
|
+
/** The binding key to resolve. */
|
|
12
16
|
key: TUseInjectableKeys;
|
|
13
17
|
target?: never;
|
|
14
18
|
}
|
|
19
|
+
/** Resolves the instance bound under the `service` namespace by `target` or `key`. A `target` bound under another namespace throws. */
|
|
15
20
|
export declare const useService: <TInstance = unknown>(opts: IUseArtifactByTarget<TInstance> | IUseArtifactByKey) => TInstance;
|
|
21
|
+
/** Resolves the instance bound under the `provider` namespace by `target` or `key`. A `target` bound under another namespace throws. */
|
|
16
22
|
export declare const useProvider: <TInstance = unknown>(opts: IUseArtifactByTarget<TInstance> | IUseArtifactByKey) => TInstance;
|
|
23
|
+
/** Resolves the instance bound under the `component` namespace by `target` or `key`. A `target` bound under another namespace throws. */
|
|
17
24
|
export declare const useComponent: <TInstance = unknown>(opts: IUseArtifactByTarget<TInstance> | IUseArtifactByKey) => TInstance;
|
|
25
|
+
/** Resolves the instance bound under the `configuration` namespace by `target` or `key`. A `target` bound under another namespace throws. */
|
|
18
26
|
export declare const useConfiguration: <TInstance = unknown>(opts: IUseArtifactByTarget<TInstance> | IUseArtifactByKey) => TInstance;
|
|
27
|
+
/**
|
|
28
|
+
* Resolves the instance bound under the `repository` namespace by `target` or `key`. A `target` bound under another namespace throws.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* import { repository, useRepository } from '@venizia/ardor';
|
|
33
|
+
*
|
|
34
|
+
* @repository()
|
|
35
|
+
* class ProductRepository {
|
|
36
|
+
* findStock(productId: string) { return productId ? 42 : 0; }
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* export function StockDisplay() {
|
|
40
|
+
* const productRepository = useRepository({ target: ProductRepository });
|
|
41
|
+
* return <span>Stock: {productRepository.findStock('p1')}</span>;
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
19
45
|
export declare const useRepository: <TInstance = unknown>(opts: IUseArtifactByTarget<TInstance> | IUseArtifactByKey) => TInstance;
|
|
20
46
|
//# sourceMappingURL=use-artifact.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BindingNamespaces } from '@venizia/ardor-kernel';
|
|
2
2
|
import { getError } from '@venizia/ignis-inversion';
|
|
3
|
-
import { useInjectable,
|
|
3
|
+
import { useInjectable, useApplicationContainer, } from './use-injectable.js';
|
|
4
4
|
/**
|
|
5
5
|
* `useService`, `useRepository`, `useProvider`, `useComponent`, `useConfiguration`. Unlike
|
|
6
6
|
* `useInjectable`, a `{ target }` must be bound under the hook's namespace, or it throws.
|
|
@@ -8,7 +8,7 @@ import { useInjectable, useInjectableContainer, } from './use-injectable.js';
|
|
|
8
8
|
const useArtifactInNamespace = (opts) => {
|
|
9
9
|
const { options, namespace, hook } = opts;
|
|
10
10
|
// Same inputs as `useInjectable`, so the check and the resolve read one container.
|
|
11
|
-
const container =
|
|
11
|
+
const container = useApplicationContainer({ container: options.container });
|
|
12
12
|
// A `key` carries its namespace in the string; only a `target` is checked.
|
|
13
13
|
if (options.target) {
|
|
14
14
|
const key = container.getMetadataRegistry().getBindingKey({ target: options.target });
|
|
@@ -22,26 +22,48 @@ const useArtifactInNamespace = (opts) => {
|
|
|
22
22
|
}
|
|
23
23
|
return useInjectable(options);
|
|
24
24
|
};
|
|
25
|
+
/** Resolves the instance bound under the `service` namespace by `target` or `key`. A `target` bound under another namespace throws. */
|
|
25
26
|
export const useService = (opts) => useArtifactInNamespace({
|
|
26
27
|
options: opts,
|
|
27
28
|
namespace: BindingNamespaces.SERVICE,
|
|
28
29
|
hook: 'useService',
|
|
29
30
|
});
|
|
31
|
+
/** Resolves the instance bound under the `provider` namespace by `target` or `key`. A `target` bound under another namespace throws. */
|
|
30
32
|
export const useProvider = (opts) => useArtifactInNamespace({
|
|
31
33
|
options: opts,
|
|
32
34
|
namespace: BindingNamespaces.PROVIDER,
|
|
33
35
|
hook: 'useProvider',
|
|
34
36
|
});
|
|
37
|
+
/** Resolves the instance bound under the `component` namespace by `target` or `key`. A `target` bound under another namespace throws. */
|
|
35
38
|
export const useComponent = (opts) => useArtifactInNamespace({
|
|
36
39
|
options: opts,
|
|
37
40
|
namespace: BindingNamespaces.COMPONENT,
|
|
38
41
|
hook: 'useComponent',
|
|
39
42
|
});
|
|
43
|
+
/** Resolves the instance bound under the `configuration` namespace by `target` or `key`. A `target` bound under another namespace throws. */
|
|
40
44
|
export const useConfiguration = (opts) => useArtifactInNamespace({
|
|
41
45
|
options: opts,
|
|
42
46
|
namespace: BindingNamespaces.CONFIGURATION,
|
|
43
47
|
hook: 'useConfiguration',
|
|
44
48
|
});
|
|
49
|
+
/**
|
|
50
|
+
* Resolves the instance bound under the `repository` namespace by `target` or `key`. A `target` bound under another namespace throws.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```tsx
|
|
54
|
+
* import { repository, useRepository } from '@venizia/ardor';
|
|
55
|
+
*
|
|
56
|
+
* @repository()
|
|
57
|
+
* class ProductRepository {
|
|
58
|
+
* findStock(productId: string) { return productId ? 42 : 0; }
|
|
59
|
+
* }
|
|
60
|
+
*
|
|
61
|
+
* export function StockDisplay() {
|
|
62
|
+
* const productRepository = useRepository({ target: ProductRepository });
|
|
63
|
+
* return <span>Stock: {productRepository.findStock('p1')}</span>;
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
45
67
|
export const useRepository = (opts) => useArtifactInNamespace({
|
|
46
68
|
options: opts,
|
|
47
69
|
namespace: BindingNamespaces.REPOSITORY,
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
/** What `useAutosave` saves, through which callback, and when. */
|
|
1
2
|
export interface IUseAutosaveOptions<TData, TReturn> {
|
|
3
|
+
/** The value to save; a change starts the `interval` wait. */
|
|
2
4
|
data: TData;
|
|
5
|
+
/** Called with the latest `data` once it has been still for `interval`. A rejection is logged, not thrown. */
|
|
3
6
|
onSave: (data: TData) => Promise<TReturn> | TReturn | void;
|
|
4
7
|
/** @default 2000 */
|
|
5
8
|
interval?: number;
|
|
9
|
+
/** Save the latest `data` on unmount. */
|
|
6
10
|
enableSaveOnUnmount?: boolean;
|
|
7
11
|
/** @default true */
|
|
8
12
|
enabled?: boolean;
|
|
9
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Calls `onSave` with `data` after it has stopped changing for `interval` milliseconds. The first
|
|
16
|
+
* render does not save. The latest `onSave` is always the one called.
|
|
17
|
+
*/
|
|
10
18
|
export declare const useAutosave: <TData, TReturn>(opts: IUseAutosaveOptions<TData, TReturn>) => void;
|
|
11
19
|
//# sourceMappingURL=use-autosave.d.ts.map
|
|
@@ -2,6 +2,10 @@ import React from 'react';
|
|
|
2
2
|
import { LoggerFactory } from '@venizia/ignis-helpers/core';
|
|
3
3
|
import { useDebounce } from './use-debounce.js';
|
|
4
4
|
const logger = LoggerFactory.getLogger(['useAutosave']);
|
|
5
|
+
/**
|
|
6
|
+
* Calls `onSave` with `data` after it has stopped changing for `interval` milliseconds. The first
|
|
7
|
+
* render does not save. The latest `onSave` is always the one called.
|
|
8
|
+
*/
|
|
5
9
|
export const useAutosave = (opts) => {
|
|
6
10
|
const { data, onSave, interval = 2000, enableSaveOnUnmount, enabled = true } = opts;
|
|
7
11
|
const valueOnCleanup = React.useRef(data);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
/** When `useBeforeUnload` asks, and the text it sets. */
|
|
1
2
|
export interface IUseBeforeUnloadOptions {
|
|
2
3
|
/** @default true */
|
|
3
4
|
enabled?: boolean;
|
|
5
|
+
/** Set as the `returnValue` of the event; browsers may show their own text instead. */
|
|
4
6
|
message?: string;
|
|
5
7
|
}
|
|
8
|
+
/** While `enabled`, asks the browser to confirm before the page is left or reloaded. */
|
|
6
9
|
export declare const useBeforeUnload: (opts: IUseBeforeUnloadOptions) => void;
|
|
7
10
|
//# sourceMappingURL=use-before-unload.d.ts.map
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
+
/** The state of one confirmation prompt, and the handlers that settle it. */
|
|
1
2
|
export interface IUseConfirmReturn {
|
|
3
|
+
/** The pending prompt text; `undefined` when none is open. */
|
|
2
4
|
message?: string;
|
|
5
|
+
/** Opens a prompt. Resolves `true` on `handleConfirm`, `false` on `handleAbort` or when another `confirm` replaces it. */
|
|
3
6
|
confirm: (opts: {
|
|
4
7
|
message: string;
|
|
5
8
|
}) => Promise<boolean>;
|
|
9
|
+
/** Closes the prompt without settling it. */
|
|
6
10
|
handleClose: () => void;
|
|
11
|
+
/** Resolves the pending `confirm` with `true` and closes. */
|
|
7
12
|
handleConfirm: () => void;
|
|
13
|
+
/** Resolves the pending `confirm` with `false` and closes. */
|
|
8
14
|
handleAbort: () => void;
|
|
9
15
|
}
|
|
16
|
+
/** State for a confirmation prompt: `confirm` returns a promise that the prompt's buttons settle. */
|
|
10
17
|
export declare const useConfirm: () => IUseConfirmReturn;
|
|
11
18
|
//# sourceMappingURL=use-confirm.d.ts.map
|
|
@@ -1,7 +1,29 @@
|
|
|
1
|
+
/** How long `useCopyToClipboard` keeps `isCopied` set. */
|
|
2
|
+
export interface IUseCopyToClipboardOptions {
|
|
3
|
+
/** Set `isCopied` back to `false` this many milliseconds after a copy; 0 or less never resets. @default 0 */
|
|
4
|
+
resetMs?: number;
|
|
5
|
+
}
|
|
6
|
+
/** The copy function of `useCopyToClipboard` and the result of its last call. */
|
|
1
7
|
export interface IUseCopyToClipboardReturn {
|
|
8
|
+
/** Resolves `true` when the text is on the clipboard and `false` otherwise, and never throws. */
|
|
2
9
|
copy: (opts: {
|
|
3
10
|
value: string;
|
|
4
11
|
}) => Promise<boolean>;
|
|
12
|
+
/** The result of the last copy, until `resetMs` has passed. */
|
|
13
|
+
isCopied: boolean;
|
|
5
14
|
}
|
|
6
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Copies text with `BrowserClipboard`; `isCopied` is the result of the last copy.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* import { useCopyToClipboard } from '@venizia/ardor';
|
|
21
|
+
*
|
|
22
|
+
* export function CopyButton() {
|
|
23
|
+
* const { copy, isCopied } = useCopyToClipboard({ resetMs: 2000 });
|
|
24
|
+
* return <button onClick={() => copy({ value: 'hello' })}>{isCopied ? 'Copied' : 'Copy'}</button>;
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare const useCopyToClipboard: (opts?: IUseCopyToClipboardOptions) => IUseCopyToClipboardReturn;
|
|
7
29
|
//# sourceMappingURL=use-copy-to-clipboard.d.ts.map
|
|
@@ -1,21 +1,45 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import { LoggerFactory } from '@venizia/ignis-helpers/core';
|
|
3
|
+
import { BrowserClipboard } from '@venizia/ardor-kernel';
|
|
2
4
|
const logger = LoggerFactory.getLogger(['useCopyToClipboard']);
|
|
3
|
-
// A hook is never handed an async function, so the copy lives outside it
|
|
4
|
-
// why the hook returns the same function on every render, as `useCallback(..., [])` did.
|
|
5
|
+
// A hook is never handed an async function, so the copy lives outside it.
|
|
5
6
|
const copyToClipboard = async (opts) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
const { value } = opts;
|
|
11
|
-
try {
|
|
12
|
-
await navigator.clipboard.writeText(value);
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
catch (error) {
|
|
16
|
-
logger.warn('[copy] Copy failed | error: %s', error);
|
|
17
|
-
return false;
|
|
7
|
+
const isCopied = await BrowserClipboard.writeText({ text: opts.value });
|
|
8
|
+
if (!isCopied) {
|
|
9
|
+
logger.warn('[copy] The clipboard did not take the text');
|
|
18
10
|
}
|
|
11
|
+
return isCopied;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Copies text with `BrowserClipboard`; `isCopied` is the result of the last copy.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* import { useCopyToClipboard } from '@venizia/ardor';
|
|
19
|
+
*
|
|
20
|
+
* export function CopyButton() {
|
|
21
|
+
* const { copy, isCopied } = useCopyToClipboard({ resetMs: 2000 });
|
|
22
|
+
* return <button onClick={() => copy({ value: 'hello' })}>{isCopied ? 'Copied' : 'Copy'}</button>;
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export const useCopyToClipboard = (opts = {}) => {
|
|
27
|
+
const { resetMs = 0 } = opts;
|
|
28
|
+
const [isCopied, setCopied] = React.useState(false);
|
|
29
|
+
const timerRef = React.useRef(undefined);
|
|
30
|
+
React.useEffect(() => {
|
|
31
|
+
return () => clearTimeout(timerRef.current);
|
|
32
|
+
}, []);
|
|
33
|
+
const copy = React.useCallback((copyOpts) => {
|
|
34
|
+
return copyToClipboard(copyOpts).then((isDone) => {
|
|
35
|
+
clearTimeout(timerRef.current);
|
|
36
|
+
setCopied(isDone);
|
|
37
|
+
if (isDone && resetMs > 0) {
|
|
38
|
+
timerRef.current = setTimeout(() => setCopied(false), resetMs);
|
|
39
|
+
}
|
|
40
|
+
return isDone;
|
|
41
|
+
});
|
|
42
|
+
}, [resetMs]);
|
|
43
|
+
return { copy, isCopied };
|
|
19
44
|
};
|
|
20
|
-
export const useCopyToClipboard = () => ({ copy: copyToClipboard });
|
|
21
45
|
//# sourceMappingURL=use-copy-to-clipboard.js.map
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
/** The value `useDebounce` follows, how long it waits, and when it runs. */
|
|
1
2
|
export interface IUseDebounceOptions<TValue> {
|
|
3
|
+
/** The value to follow; the output changes once it has been still for `delay`. */
|
|
2
4
|
value: TValue;
|
|
5
|
+
/** Milliseconds to wait after the last change. @default 500 */
|
|
3
6
|
delay?: number;
|
|
4
7
|
/** @default true */
|
|
5
8
|
enabled?: boolean;
|
|
6
9
|
}
|
|
10
|
+
/** The settled value of `useDebounce`. */
|
|
7
11
|
export interface IUseDebounceReturn<TValue> {
|
|
12
|
+
/** The last `value` that stayed unchanged for `delay`; the first `value` until then. */
|
|
8
13
|
debouncedValue: TValue;
|
|
9
14
|
}
|
|
15
|
+
/** Follows `value` after it has stopped changing for `delay`. Outside a browser, or when not `enabled`, it never updates. */
|
|
10
16
|
export declare const useDebounce: <TValue>(opts: IUseDebounceOptions<TValue>) => IUseDebounceReturn<TValue>;
|
|
11
17
|
//# sourceMappingURL=use-debounce.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { App, BrowserUtility } from '@venizia/ardor-kernel';
|
|
3
|
+
/** Follows `value` after it has stopped changing for `delay`. Outside a browser, or when not `enabled`, it never updates. */
|
|
3
4
|
export const useDebounce = (opts) => {
|
|
4
5
|
const { value, delay, enabled = true } = opts;
|
|
5
6
|
const [debouncedValue, setDebouncedValue] = React.useState(value);
|