@pallasoft/ps-core-components 2.46.1 → 2.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ps-data-boundary.d.ts +2 -1
- package/dist/components/ps-data-boundary.js +2 -2
- package/dist/components/ps-empty-value.d.ts +5 -0
- package/dist/components/ps-empty-value.js +8 -0
- package/dist/components/ps-image.d.ts +14 -0
- package/dist/components/ps-image.js +29 -0
- package/dist/components/ps-signed-value.d.ts +6 -0
- package/dist/components/ps-signed-value.js +9 -0
- package/dist/constants/ps-empty-value-config.d.ts +4 -0
- package/dist/constants/ps-empty-value-config.js +7 -0
- package/dist/constants/ps-image-config.d.ts +7 -0
- package/dist/constants/ps-image-config.js +10 -0
- package/dist/constants/ps-image-fit.d.ts +7 -0
- package/dist/constants/ps-image-fit.js +11 -0
- package/dist/constants/ps-image-status.d.ts +5 -0
- package/dist/constants/ps-image-status.js +9 -0
- package/dist/constants/ps-signed-value-classes.d.ts +2 -0
- package/dist/constants/ps-signed-value-classes.js +9 -0
- package/dist/esm/components/ps-data-boundary.js +2 -2
- package/dist/esm/components/ps-empty-value.js +4 -0
- package/dist/esm/components/ps-image.js +25 -0
- package/dist/esm/components/ps-signed-value.js +5 -0
- package/dist/esm/constants/ps-empty-value-config.js +4 -0
- package/dist/esm/constants/ps-image-config.js +7 -0
- package/dist/esm/constants/ps-image-fit.js +8 -0
- package/dist/esm/constants/ps-image-status.js +6 -0
- package/dist/esm/constants/ps-signed-value-classes.js +6 -0
- package/dist/esm/hooks/use-ps-cursor-pages.js +82 -0
- package/dist/esm/hooks/use-ps-draft-state.js +18 -0
- package/dist/esm/hooks/use-ps-optimistic-fetcher.js +2 -4
- package/dist/esm/hooks/use-ps-optimistic-save.js +1 -1
- package/dist/esm/hooks/use-ps-remote-read.js +31 -0
- package/dist/esm/hooks/use-ps-text-param.js +16 -0
- package/dist/esm/index.js +32 -12
- package/dist/esm/models/ps-image-state-data.js +1 -0
- package/dist/esm/models/ps-remote-read-state.js +1 -0
- package/dist/esm/models/ps-remote-settled-data.js +1 -0
- package/dist/esm/models/ps-text-param-data.js +1 -0
- package/dist/esm/models/ps-text-param-options.js +1 -0
- package/dist/esm/services/ps-cursor-pages-service.js +14 -0
- package/dist/esm/services/ps-draft-state-service.js +11 -0
- package/dist/esm/services/ps-image-service.js +11 -0
- package/dist/esm/services/ps-remote-read-service.js +19 -0
- package/dist/esm/services/ps-signed-value-service.js +13 -0
- package/dist/hooks/use-ps-cursor-pages.d.ts +3 -0
- package/dist/hooks/use-ps-cursor-pages.js +86 -0
- package/dist/hooks/use-ps-draft-state.d.ts +3 -0
- package/dist/hooks/use-ps-draft-state.js +22 -0
- package/dist/hooks/use-ps-optimistic-fetcher.js +7 -9
- package/dist/hooks/use-ps-optimistic-save.js +4 -4
- package/dist/hooks/use-ps-remote-read.d.ts +3 -0
- package/dist/hooks/use-ps-remote-read.js +35 -0
- package/dist/hooks/use-ps-text-param.d.ts +4 -0
- package/dist/hooks/use-ps-text-param.js +20 -0
- package/dist/index.d.ts +32 -12
- package/dist/index.js +39 -12
- package/dist/interfaces/ps-cursor-pages-input.d.ts +8 -0
- package/dist/interfaces/ps-cursor-pages-result.d.ts +7 -0
- package/dist/interfaces/ps-draft-state-options.d.ts +3 -0
- package/dist/interfaces/ps-draft-state-result.d.ts +7 -0
- package/dist/interfaces/ps-optimistic-failure.d.ts +1 -1
- package/dist/interfaces/ps-optimistic-save-input.d.ts +1 -1
- package/dist/interfaces/ps-remote-read-input.d.ts +5 -0
- package/dist/interfaces/ps-remote-read-result.d.ts +5 -0
- package/dist/models/ps-cursor-pages-state.d.ts +8 -0
- package/dist/models/ps-draft-state-data.d.ts +4 -0
- package/dist/models/ps-image-state-data.d.ts +5 -0
- package/dist/models/ps-image-state-data.js +2 -0
- package/dist/models/ps-remote-read-state.d.ts +6 -0
- package/dist/models/ps-remote-read-state.js +2 -0
- package/dist/models/ps-remote-settled-data.d.ts +4 -0
- package/dist/models/ps-remote-settled-data.js +2 -0
- package/dist/models/ps-text-param-data.d.ts +5 -0
- package/dist/models/ps-text-param-data.js +2 -0
- package/dist/models/ps-text-param-options.d.ts +5 -0
- package/dist/models/ps-text-param-options.js +2 -0
- package/dist/services/ps-cursor-pages-service.d.ts +6 -0
- package/dist/services/ps-cursor-pages-service.js +18 -0
- package/dist/services/ps-draft-state-service.d.ts +6 -0
- package/dist/services/ps-draft-state-service.js +15 -0
- package/dist/services/ps-image-service.d.ts +6 -0
- package/dist/services/ps-image-service.js +15 -0
- package/dist/services/ps-remote-read-service.d.ts +8 -0
- package/dist/services/ps-remote-read-service.js +23 -0
- package/dist/services/ps-signed-value-service.d.ts +5 -0
- package/dist/services/ps-signed-value-service.js +17 -0
- package/package.json +2 -2
- package/dist/constants/ps-optimistic-config.d.ts +0 -5
- package/dist/constants/ps-optimistic-config.js +0 -8
- package/dist/esm/constants/ps-optimistic-config.js +0 -5
- package/dist/esm/hooks/use-ps-change-queue.js +0 -36
- package/dist/esm/hooks/use-ps-debounced-value.js +0 -11
- package/dist/esm/hooks/use-ps-optimistic-rows.js +0 -29
- package/dist/esm/services/ps-deep-equal-service.js +0 -31
- package/dist/esm/services/ps-optimistic-fetcher-service.js +0 -12
- package/dist/esm/services/ps-optimistic-release-service.js +0 -38
- package/dist/esm/services/ps-optimistic-save-service.js +0 -64
- package/dist/hooks/use-ps-change-queue.d.ts +0 -3
- package/dist/hooks/use-ps-change-queue.js +0 -40
- package/dist/hooks/use-ps-debounced-value.d.ts +0 -1
- package/dist/hooks/use-ps-debounced-value.js +0 -15
- package/dist/hooks/use-ps-optimistic-rows.d.ts +0 -3
- package/dist/hooks/use-ps-optimistic-rows.js +0 -33
- package/dist/interfaces/ps-change-queue-input.d.ts +0 -5
- package/dist/interfaces/ps-change-queue-result.d.ts +0 -3
- package/dist/interfaces/ps-optimistic-changes.d.ts +0 -7
- package/dist/interfaces/ps-optimistic-failure-reason.d.ts +0 -1
- package/dist/interfaces/ps-optimistic-outcome.d.ts +0 -5
- package/dist/interfaces/ps-optimistic-rows-input.d.ts +0 -6
- package/dist/interfaces/ps-optimistic-rows-result.d.ts +0 -6
- package/dist/interfaces/ps-optimistic-save-spec.d.ts +0 -11
- package/dist/services/ps-deep-equal-service.d.ts +0 -5
- package/dist/services/ps-deep-equal-service.js +0 -35
- package/dist/services/ps-optimistic-fetcher-service.d.ts +0 -6
- package/dist/services/ps-optimistic-fetcher-service.js +0 -16
- package/dist/services/ps-optimistic-release-service.d.ts +0 -5
- package/dist/services/ps-optimistic-release-service.js +0 -42
- package/dist/services/ps-optimistic-save-service.d.ts +0 -11
- package/dist/services/ps-optimistic-save-service.js +0 -68
- /package/dist/esm/interfaces/{ps-change-queue-input.js → ps-cursor-pages-input.js} +0 -0
- /package/dist/esm/interfaces/{ps-change-queue-result.js → ps-cursor-pages-result.js} +0 -0
- /package/dist/esm/interfaces/{ps-optimistic-changes.js → ps-draft-state-options.js} +0 -0
- /package/dist/esm/interfaces/{ps-optimistic-failure-reason.js → ps-draft-state-result.js} +0 -0
- /package/dist/esm/interfaces/{ps-optimistic-outcome.js → ps-remote-read-input.js} +0 -0
- /package/dist/esm/interfaces/{ps-optimistic-rows-input.js → ps-remote-read-result.js} +0 -0
- /package/dist/esm/{interfaces/ps-optimistic-rows-result.js → models/ps-cursor-pages-state.js} +0 -0
- /package/dist/esm/{interfaces/ps-optimistic-save-spec.js → models/ps-draft-state-data.js} +0 -0
- /package/dist/interfaces/{ps-change-queue-input.js → ps-cursor-pages-input.js} +0 -0
- /package/dist/interfaces/{ps-change-queue-result.js → ps-cursor-pages-result.js} +0 -0
- /package/dist/interfaces/{ps-optimistic-changes.js → ps-draft-state-options.js} +0 -0
- /package/dist/interfaces/{ps-optimistic-failure-reason.js → ps-draft-state-result.js} +0 -0
- /package/dist/interfaces/{ps-optimistic-outcome.js → ps-remote-read-input.js} +0 -0
- /package/dist/interfaces/{ps-optimistic-rows-input.js → ps-remote-read-result.js} +0 -0
- /package/dist/{interfaces/ps-optimistic-rows-result.js → models/ps-cursor-pages-state.js} +0 -0
- /package/dist/{interfaces/ps-optimistic-save-spec.js → models/ps-draft-state-data.js} +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsCursorPagesService = void 0;
|
|
4
|
+
class PsCursorPagesService {
|
|
5
|
+
static append(existing, page, keyOf) {
|
|
6
|
+
if (keyOf === undefined)
|
|
7
|
+
return [...existing, ...page];
|
|
8
|
+
const known = new Set(existing.map(keyOf));
|
|
9
|
+
return [...existing, ...page.filter((row) => !known.has(keyOf(row)))];
|
|
10
|
+
}
|
|
11
|
+
static hasMore(pageLength, pageSize) {
|
|
12
|
+
return pageLength >= pageSize;
|
|
13
|
+
}
|
|
14
|
+
static initialState() {
|
|
15
|
+
return { error: null, hasMore: false, isLoadingMore: false, rows: [], settled: null };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.PsCursorPagesService = PsCursorPagesService;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { PsDraftStateOptions } from '../interfaces/ps-draft-state-options';
|
|
2
|
+
import type { PsDraftStateData } from '../models/ps-draft-state-data';
|
|
3
|
+
export declare class PsDraftStateService {
|
|
4
|
+
static fresh<Value>(initial: Value): PsDraftStateData<Value>;
|
|
5
|
+
static isEqual<Value>(left: Value, right: Value, options: PsDraftStateOptions<Value> | undefined): boolean;
|
|
6
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsDraftStateService = void 0;
|
|
4
|
+
const ps_utilities_1 = require("@pallasoft/ps-utilities");
|
|
5
|
+
class PsDraftStateService {
|
|
6
|
+
static fresh(initial) {
|
|
7
|
+
return { baseline: initial, value: structuredClone(initial) };
|
|
8
|
+
}
|
|
9
|
+
static isEqual(left, right, options) {
|
|
10
|
+
if (options !== undefined && options.isEqual !== undefined)
|
|
11
|
+
return options.isEqual(left, right);
|
|
12
|
+
return ps_utilities_1.PsDeepEqualService.isEqual(left, right);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.PsDraftStateService = PsDraftStateService;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { PsImageStatus } from '../constants/ps-image-status';
|
|
2
|
+
import type { PsImageStateData } from '../models/ps-image-state-data';
|
|
3
|
+
export declare class PsImageService {
|
|
4
|
+
static resolve(state: PsImageStateData, src: string): PsImageStatus;
|
|
5
|
+
static statusOf(element: HTMLImageElement): PsImageStatus;
|
|
6
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsImageService = void 0;
|
|
4
|
+
const ps_image_status_1 = require("../constants/ps-image-status");
|
|
5
|
+
class PsImageService {
|
|
6
|
+
static resolve(state, src) {
|
|
7
|
+
return state.src === src ? state.status : ps_image_status_1.PsImageStatus.Loading;
|
|
8
|
+
}
|
|
9
|
+
static statusOf(element) {
|
|
10
|
+
if (!element.complete)
|
|
11
|
+
return ps_image_status_1.PsImageStatus.Loading;
|
|
12
|
+
return element.naturalWidth > 0 ? ps_image_status_1.PsImageStatus.Loaded : ps_image_status_1.PsImageStatus.Error;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.PsImageService = PsImageService;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PsRemoteReadState } from '../models/ps-remote-read-state';
|
|
2
|
+
import type { PsRemoteSettledData } from '../models/ps-remote-settled-data';
|
|
3
|
+
export declare class PsRemoteReadService {
|
|
4
|
+
private static readonly client;
|
|
5
|
+
static initialState<Data>(): PsRemoteReadState<Data>;
|
|
6
|
+
static isPending(settled: PsRemoteSettledData | null, url: string | null, reloadKey: unknown): boolean;
|
|
7
|
+
static read<Data>(url: string, parse: (value: unknown) => Data, signal: AbortSignal): Promise<Data>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsRemoteReadService = void 0;
|
|
4
|
+
const ps_utilities_1 = require("@pallasoft/ps-utilities");
|
|
5
|
+
class PsRemoteReadService {
|
|
6
|
+
static initialState() {
|
|
7
|
+
return { data: null, error: null, settled: null };
|
|
8
|
+
}
|
|
9
|
+
static isPending(settled, url, reloadKey) {
|
|
10
|
+
if (url === null)
|
|
11
|
+
return false;
|
|
12
|
+
return settled === null || settled.url !== url || !Object.is(settled.reloadKey, reloadKey);
|
|
13
|
+
}
|
|
14
|
+
static async read(url, parse, signal) {
|
|
15
|
+
const response = await PsRemoteReadService.client.get(url, {
|
|
16
|
+
responseType: 'json',
|
|
17
|
+
signal,
|
|
18
|
+
});
|
|
19
|
+
return parse(response.data);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.PsRemoteReadService = PsRemoteReadService;
|
|
23
|
+
PsRemoteReadService.client = ps_utilities_1.Client.HttpService.createApiClient();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsSignedValueService = void 0;
|
|
4
|
+
const ps_utilities_1 = require("@pallasoft/ps-utilities");
|
|
5
|
+
class PsSignedValueService {
|
|
6
|
+
static direction(value) {
|
|
7
|
+
const sign = ps_utilities_1.Common.DecimalStringService.sign(value);
|
|
8
|
+
if (sign > 0)
|
|
9
|
+
return ps_utilities_1.AmountDirection.In;
|
|
10
|
+
return sign < 0 ? ps_utilities_1.AmountDirection.Out : ps_utilities_1.AmountDirection.Neutral;
|
|
11
|
+
}
|
|
12
|
+
static text(value, format) {
|
|
13
|
+
const body = format === undefined ? value : format(value);
|
|
14
|
+
return ps_utilities_1.Common.DecimalStringService.sign(value) > 0 ? `+${body}` : body;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.PsSignedValueService = PsSignedValueService;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pallasoft/ps-core-components",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.47.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"@dnd-kit/utilities": "^3.2.2",
|
|
49
49
|
"@pallasoft/ps-icons": "^1.5.4",
|
|
50
50
|
"@pallasoft/ps-motion-components": "^1.2.2",
|
|
51
|
-
"@pallasoft/ps-utilities": "^2.
|
|
51
|
+
"@pallasoft/ps-utilities": "^2.29.0",
|
|
52
52
|
"@portabletext/react": "^3.2.4",
|
|
53
53
|
"@portabletext/types": "^2.0.13",
|
|
54
54
|
"@radix-ui/react-accordion": "^1.2.12",
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
-
export const usePsChangeQueue = ({ onDrained, onFailed, send, }) => {
|
|
3
|
-
const pending = useRef([]);
|
|
4
|
-
const isRunning = useRef(false);
|
|
5
|
-
const latest = useRef({ onDrained, onFailed, send });
|
|
6
|
-
useEffect(() => {
|
|
7
|
-
latest.current = { onDrained, onFailed, send };
|
|
8
|
-
}, [onDrained, onFailed, send]);
|
|
9
|
-
const run = useCallback(async () => {
|
|
10
|
-
if (isRunning.current)
|
|
11
|
-
return;
|
|
12
|
-
isRunning.current = true;
|
|
13
|
-
await Promise.resolve();
|
|
14
|
-
while (pending.current.length > 0) {
|
|
15
|
-
const batch = pending.current;
|
|
16
|
-
pending.current = [];
|
|
17
|
-
await new Promise((resolve) => resolve(latest.current.send(batch))).catch(() => {
|
|
18
|
-
const dropped = pending.current;
|
|
19
|
-
pending.current = [];
|
|
20
|
-
try {
|
|
21
|
-
latest.current.onFailed(batch, dropped);
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
isRunning.current = false;
|
|
29
|
-
latest.current.onDrained();
|
|
30
|
-
}, []);
|
|
31
|
-
const enqueue = useCallback((change) => {
|
|
32
|
-
pending.current = [...pending.current, change];
|
|
33
|
-
void run();
|
|
34
|
-
}, [run]);
|
|
35
|
-
return { enqueue };
|
|
36
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
import { Client } from '@pallasoft/ps-utilities';
|
|
3
|
-
export const usePsDebouncedValue = (value, waitMs) => {
|
|
4
|
-
const [settled, setSettled] = useState(value);
|
|
5
|
-
useEffect(() => {
|
|
6
|
-
const debounced = Client.DebounceService.create((next) => setSettled(next), waitMs);
|
|
7
|
-
debounced.run(value);
|
|
8
|
-
return debounced.cancel;
|
|
9
|
-
}, [value, waitMs]);
|
|
10
|
-
return settled;
|
|
11
|
-
};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import { PsOptimisticConfig } from '../constants/ps-optimistic-config.js';
|
|
3
|
-
import { PsOptimisticReleaseService } from '../services/ps-optimistic-release-service.js';
|
|
4
|
-
export const usePsOptimisticRows = ({ apply, isHolding = false, isSaving, rows, }) => {
|
|
5
|
-
const [remembered, setRemembered] = useState(() => PsOptimisticReleaseService.empty(rows));
|
|
6
|
-
const isActive = isSaving || isHolding;
|
|
7
|
-
const current = PsOptimisticReleaseService.next(remembered, rows, isActive, isHolding, apply);
|
|
8
|
-
if (current !== remembered)
|
|
9
|
-
setRemembered(current);
|
|
10
|
-
const { changes } = current;
|
|
11
|
-
const isHeld = changes.length > 0 && !isActive;
|
|
12
|
-
const shown = useMemo(() => (changes.length === 0 ? rows : apply(rows, changes)), [apply, changes, rows]);
|
|
13
|
-
useEffect(() => {
|
|
14
|
-
if (!isHeld)
|
|
15
|
-
return undefined;
|
|
16
|
-
const timer = setTimeout(() => setRemembered(PsOptimisticReleaseService.empty(rows)), PsOptimisticConfig.releaseTimeoutMs);
|
|
17
|
-
return () => clearTimeout(timer);
|
|
18
|
-
}, [isHeld, changes, rows]);
|
|
19
|
-
const remember = useCallback((change) => setRemembered((previous) => ({
|
|
20
|
-
base: rows,
|
|
21
|
-
changes: [...previous.changes, change],
|
|
22
|
-
drainBase: null,
|
|
23
|
-
wasActive: previous.wasActive,
|
|
24
|
-
wasHolding: previous.wasHolding,
|
|
25
|
-
})), [rows]);
|
|
26
|
-
const forgetLast = useCallback(() => setRemembered((previous) => ({ ...previous, changes: previous.changes.slice(0, -1) })), []);
|
|
27
|
-
const rollback = useCallback(() => setRemembered(PsOptimisticReleaseService.empty(rows)), [rows]);
|
|
28
|
-
return { forgetLast, remember, rollback, rows: shown };
|
|
29
|
-
};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
export class PsDeepEqualService {
|
|
2
|
-
static isEqual(left, right) {
|
|
3
|
-
if (Object.is(left, right))
|
|
4
|
-
return true;
|
|
5
|
-
if (typeof left !== 'object' || typeof right !== 'object')
|
|
6
|
-
return false;
|
|
7
|
-
if (left === null || right === null)
|
|
8
|
-
return false;
|
|
9
|
-
if (Array.isArray(left) || Array.isArray(right))
|
|
10
|
-
return PsDeepEqualService.isArrayEqual(left, right);
|
|
11
|
-
if (left instanceof Date || right instanceof Date) {
|
|
12
|
-
return left instanceof Date && right instanceof Date && left.getTime() === right.getTime();
|
|
13
|
-
}
|
|
14
|
-
return PsDeepEqualService.isRecordEqual(left, right);
|
|
15
|
-
}
|
|
16
|
-
static isArrayEqual(left, right) {
|
|
17
|
-
if (!Array.isArray(left) || !Array.isArray(right))
|
|
18
|
-
return false;
|
|
19
|
-
if (left.length !== right.length)
|
|
20
|
-
return false;
|
|
21
|
-
return left.every((item, index) => PsDeepEqualService.isEqual(item, right[index]));
|
|
22
|
-
}
|
|
23
|
-
static isRecordEqual(left, right) {
|
|
24
|
-
const leftEntries = Object.entries(left);
|
|
25
|
-
const rightRecord = { ...right };
|
|
26
|
-
if (leftEntries.length !== Object.keys(rightRecord).length)
|
|
27
|
-
return false;
|
|
28
|
-
return leftEntries.every(([key, value]) => Object.prototype.hasOwnProperty.call(rightRecord, key) &&
|
|
29
|
-
PsDeepEqualService.isEqual(value, rightRecord[key]));
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export class PsOptimisticFetcherService {
|
|
2
|
-
static outcome(data, before) {
|
|
3
|
-
if (data === before)
|
|
4
|
-
return { data: undefined, reason: 'no-response' };
|
|
5
|
-
return { data, reason: 'settled' };
|
|
6
|
-
}
|
|
7
|
-
static failureReason(outcome, isSuccess) {
|
|
8
|
-
if (outcome.reason !== 'settled')
|
|
9
|
-
return outcome.reason;
|
|
10
|
-
return isSuccess(outcome.data) ? null : 'rejected';
|
|
11
|
-
}
|
|
12
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { PsDeepEqualService } from './ps-deep-equal-service.js';
|
|
2
|
-
export class PsOptimisticReleaseService {
|
|
3
|
-
static empty(rows) {
|
|
4
|
-
return { base: rows, changes: [], drainBase: null, wasActive: false, wasHolding: false };
|
|
5
|
-
}
|
|
6
|
-
static next(remembered, rows, isActive, isHolding, apply) {
|
|
7
|
-
if (isActive) {
|
|
8
|
-
const wasHolding = remembered.wasHolding || isHolding;
|
|
9
|
-
if (remembered.wasActive &&
|
|
10
|
-
remembered.drainBase === null &&
|
|
11
|
-
remembered.wasHolding === wasHolding)
|
|
12
|
-
return remembered;
|
|
13
|
-
return { ...remembered, drainBase: null, wasActive: true, wasHolding };
|
|
14
|
-
}
|
|
15
|
-
if (remembered.changes.length === 0) {
|
|
16
|
-
if (remembered.base === rows && !remembered.wasActive)
|
|
17
|
-
return remembered;
|
|
18
|
-
return PsOptimisticReleaseService.empty(rows);
|
|
19
|
-
}
|
|
20
|
-
if (remembered.wasActive) {
|
|
21
|
-
if (remembered.wasHolding && rows !== remembered.base) {
|
|
22
|
-
return PsOptimisticReleaseService.empty(rows);
|
|
23
|
-
}
|
|
24
|
-
if (!PsDeepEqualService.isEqual(rows, remembered.base)) {
|
|
25
|
-
return PsOptimisticReleaseService.empty(rows);
|
|
26
|
-
}
|
|
27
|
-
return { ...remembered, drainBase: rows, wasActive: false, wasHolding: false };
|
|
28
|
-
}
|
|
29
|
-
const reference = remembered.drainBase === null ? remembered.base : remembered.drainBase;
|
|
30
|
-
if (!PsDeepEqualService.isEqual(rows, reference)) {
|
|
31
|
-
return PsOptimisticReleaseService.empty(rows);
|
|
32
|
-
}
|
|
33
|
-
if (PsDeepEqualService.isEqual(apply(rows, remembered.changes), rows)) {
|
|
34
|
-
return PsOptimisticReleaseService.empty(rows);
|
|
35
|
-
}
|
|
36
|
-
return remembered;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { PsOptimisticConfig } from '../constants/ps-optimistic-config.js';
|
|
2
|
-
export class PsOptimisticSaveService {
|
|
3
|
-
static isPending(id) {
|
|
4
|
-
return id.startsWith(PsOptimisticConfig.pendingPrefix);
|
|
5
|
-
}
|
|
6
|
-
static pendingId() {
|
|
7
|
-
return `${PsOptimisticConfig.pendingPrefix}${crypto.randomUUID()}`;
|
|
8
|
-
}
|
|
9
|
-
static saveValues(spec, values, existingId, scopeId) {
|
|
10
|
-
const fields = spec.isFlat
|
|
11
|
-
? Object.fromEntries(Object.entries(values).map(([key, value]) => [key, String(value)]))
|
|
12
|
-
: { values: JSON.stringify(values) };
|
|
13
|
-
return {
|
|
14
|
-
...fields,
|
|
15
|
-
[spec.idField]: existingId,
|
|
16
|
-
intent: spec.intent,
|
|
17
|
-
pendingId: PsOptimisticSaveService.pendingId(),
|
|
18
|
-
scopeId,
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
static removeValues(spec, ids) {
|
|
22
|
-
return spec.remove === undefined
|
|
23
|
-
? {}
|
|
24
|
-
: { [spec.remove.idsField]: JSON.stringify(ids), intent: spec.remove.intent };
|
|
25
|
-
}
|
|
26
|
-
static apply(rows, changes, spec) {
|
|
27
|
-
return changes.reduce((current, change) => {
|
|
28
|
-
if (change.intent === spec.intent)
|
|
29
|
-
return PsOptimisticSaveService.saved(current, change, spec);
|
|
30
|
-
if (spec.remove !== undefined && change.intent === spec.remove.intent)
|
|
31
|
-
return PsOptimisticSaveService.removed(current, change, spec, spec.remove.idsField);
|
|
32
|
-
return current;
|
|
33
|
-
}, rows);
|
|
34
|
-
}
|
|
35
|
-
static read(raw) {
|
|
36
|
-
if (raw === undefined)
|
|
37
|
-
return null;
|
|
38
|
-
try {
|
|
39
|
-
return JSON.parse(raw);
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
static saved(rows, change, spec) {
|
|
46
|
-
const parsed = spec.isFlat ? change : PsOptimisticSaveService.read(change.values);
|
|
47
|
-
if (parsed === null || typeof parsed !== 'object')
|
|
48
|
-
return rows;
|
|
49
|
-
const existingId = change[spec.idField];
|
|
50
|
-
const id = existingId === '' ? change.pendingId : existingId;
|
|
51
|
-
const previous = rows.find((row) => spec.getId(row) === existingId);
|
|
52
|
-
const built = spec.build(parsed, id, previous === undefined ? null : previous, change.scopeId);
|
|
53
|
-
if (built === null)
|
|
54
|
-
return rows;
|
|
55
|
-
return previous === undefined
|
|
56
|
-
? [...rows, built]
|
|
57
|
-
: rows.map((row) => (spec.getId(row) === existingId ? built : row));
|
|
58
|
-
}
|
|
59
|
-
static removed(rows, change, spec, idsField) {
|
|
60
|
-
const parsed = PsOptimisticSaveService.read(change[idsField]);
|
|
61
|
-
const ids = new Set(Array.isArray(parsed) ? parsed : []);
|
|
62
|
-
return rows.filter((row) => !ids.has(spec.getId(row)));
|
|
63
|
-
}
|
|
64
|
-
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import type { PsChangeQueueInput } from '../interfaces/ps-change-queue-input';
|
|
2
|
-
import type { PsChangeQueueResult } from '../interfaces/ps-change-queue-result';
|
|
3
|
-
export declare const usePsChangeQueue: <Change>({ onDrained, onFailed, send, }: PsChangeQueueInput<Change>) => PsChangeQueueResult<Change>;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.usePsChangeQueue = void 0;
|
|
4
|
-
const react_1 = require("react");
|
|
5
|
-
const usePsChangeQueue = ({ onDrained, onFailed, send, }) => {
|
|
6
|
-
const pending = (0, react_1.useRef)([]);
|
|
7
|
-
const isRunning = (0, react_1.useRef)(false);
|
|
8
|
-
const latest = (0, react_1.useRef)({ onDrained, onFailed, send });
|
|
9
|
-
(0, react_1.useEffect)(() => {
|
|
10
|
-
latest.current = { onDrained, onFailed, send };
|
|
11
|
-
}, [onDrained, onFailed, send]);
|
|
12
|
-
const run = (0, react_1.useCallback)(async () => {
|
|
13
|
-
if (isRunning.current)
|
|
14
|
-
return;
|
|
15
|
-
isRunning.current = true;
|
|
16
|
-
await Promise.resolve();
|
|
17
|
-
while (pending.current.length > 0) {
|
|
18
|
-
const batch = pending.current;
|
|
19
|
-
pending.current = [];
|
|
20
|
-
await new Promise((resolve) => resolve(latest.current.send(batch))).catch(() => {
|
|
21
|
-
const dropped = pending.current;
|
|
22
|
-
pending.current = [];
|
|
23
|
-
try {
|
|
24
|
-
latest.current.onFailed(batch, dropped);
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
isRunning.current = false;
|
|
32
|
-
latest.current.onDrained();
|
|
33
|
-
}, []);
|
|
34
|
-
const enqueue = (0, react_1.useCallback)((change) => {
|
|
35
|
-
pending.current = [...pending.current, change];
|
|
36
|
-
void run();
|
|
37
|
-
}, [run]);
|
|
38
|
-
return { enqueue };
|
|
39
|
-
};
|
|
40
|
-
exports.usePsChangeQueue = usePsChangeQueue;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const usePsDebouncedValue: <T>(value: T, waitMs: number) => T;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.usePsDebouncedValue = void 0;
|
|
4
|
-
const react_1 = require("react");
|
|
5
|
-
const ps_utilities_1 = require("@pallasoft/ps-utilities");
|
|
6
|
-
const usePsDebouncedValue = (value, waitMs) => {
|
|
7
|
-
const [settled, setSettled] = (0, react_1.useState)(value);
|
|
8
|
-
(0, react_1.useEffect)(() => {
|
|
9
|
-
const debounced = ps_utilities_1.Client.DebounceService.create((next) => setSettled(next), waitMs);
|
|
10
|
-
debounced.run(value);
|
|
11
|
-
return debounced.cancel;
|
|
12
|
-
}, [value, waitMs]);
|
|
13
|
-
return settled;
|
|
14
|
-
};
|
|
15
|
-
exports.usePsDebouncedValue = usePsDebouncedValue;
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import type { PsOptimisticRowsInput } from '../interfaces/ps-optimistic-rows-input';
|
|
2
|
-
import type { PsOptimisticRowsResult } from '../interfaces/ps-optimistic-rows-result';
|
|
3
|
-
export declare const usePsOptimisticRows: <Row, Change>({ apply, isHolding, isSaving, rows, }: PsOptimisticRowsInput<Row, Change>) => PsOptimisticRowsResult<Row, Change>;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.usePsOptimisticRows = void 0;
|
|
4
|
-
const react_1 = require("react");
|
|
5
|
-
const ps_optimistic_config_1 = require("../constants/ps-optimistic-config");
|
|
6
|
-
const ps_optimistic_release_service_1 = require("../services/ps-optimistic-release-service");
|
|
7
|
-
const usePsOptimisticRows = ({ apply, isHolding = false, isSaving, rows, }) => {
|
|
8
|
-
const [remembered, setRemembered] = (0, react_1.useState)(() => ps_optimistic_release_service_1.PsOptimisticReleaseService.empty(rows));
|
|
9
|
-
const isActive = isSaving || isHolding;
|
|
10
|
-
const current = ps_optimistic_release_service_1.PsOptimisticReleaseService.next(remembered, rows, isActive, isHolding, apply);
|
|
11
|
-
if (current !== remembered)
|
|
12
|
-
setRemembered(current);
|
|
13
|
-
const { changes } = current;
|
|
14
|
-
const isHeld = changes.length > 0 && !isActive;
|
|
15
|
-
const shown = (0, react_1.useMemo)(() => (changes.length === 0 ? rows : apply(rows, changes)), [apply, changes, rows]);
|
|
16
|
-
(0, react_1.useEffect)(() => {
|
|
17
|
-
if (!isHeld)
|
|
18
|
-
return undefined;
|
|
19
|
-
const timer = setTimeout(() => setRemembered(ps_optimistic_release_service_1.PsOptimisticReleaseService.empty(rows)), ps_optimistic_config_1.PsOptimisticConfig.releaseTimeoutMs);
|
|
20
|
-
return () => clearTimeout(timer);
|
|
21
|
-
}, [isHeld, changes, rows]);
|
|
22
|
-
const remember = (0, react_1.useCallback)((change) => setRemembered((previous) => ({
|
|
23
|
-
base: rows,
|
|
24
|
-
changes: [...previous.changes, change],
|
|
25
|
-
drainBase: null,
|
|
26
|
-
wasActive: previous.wasActive,
|
|
27
|
-
wasHolding: previous.wasHolding,
|
|
28
|
-
})), [rows]);
|
|
29
|
-
const forgetLast = (0, react_1.useCallback)(() => setRemembered((previous) => ({ ...previous, changes: previous.changes.slice(0, -1) })), []);
|
|
30
|
-
const rollback = (0, react_1.useCallback)(() => setRemembered(ps_optimistic_release_service_1.PsOptimisticReleaseService.empty(rows)), [rows]);
|
|
31
|
-
return { forgetLast, remember, rollback, rows: shown };
|
|
32
|
-
};
|
|
33
|
-
exports.usePsOptimisticRows = usePsOptimisticRows;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type PsOptimisticFailureReason = 'error' | 'no-response' | 'rejected' | 'timeout' | 'unmounted';
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export interface PsOptimisticSaveSpec<Row, Values> {
|
|
2
|
-
build: (values: Values, id: string, previous: Row | null, scopeId: string) => Row | null;
|
|
3
|
-
getId: (row: Row) => string;
|
|
4
|
-
idField: string;
|
|
5
|
-
intent: string;
|
|
6
|
-
isFlat: boolean;
|
|
7
|
-
remove?: {
|
|
8
|
-
idsField: string;
|
|
9
|
-
intent: string;
|
|
10
|
-
};
|
|
11
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PsDeepEqualService = void 0;
|
|
4
|
-
class PsDeepEqualService {
|
|
5
|
-
static isEqual(left, right) {
|
|
6
|
-
if (Object.is(left, right))
|
|
7
|
-
return true;
|
|
8
|
-
if (typeof left !== 'object' || typeof right !== 'object')
|
|
9
|
-
return false;
|
|
10
|
-
if (left === null || right === null)
|
|
11
|
-
return false;
|
|
12
|
-
if (Array.isArray(left) || Array.isArray(right))
|
|
13
|
-
return PsDeepEqualService.isArrayEqual(left, right);
|
|
14
|
-
if (left instanceof Date || right instanceof Date) {
|
|
15
|
-
return left instanceof Date && right instanceof Date && left.getTime() === right.getTime();
|
|
16
|
-
}
|
|
17
|
-
return PsDeepEqualService.isRecordEqual(left, right);
|
|
18
|
-
}
|
|
19
|
-
static isArrayEqual(left, right) {
|
|
20
|
-
if (!Array.isArray(left) || !Array.isArray(right))
|
|
21
|
-
return false;
|
|
22
|
-
if (left.length !== right.length)
|
|
23
|
-
return false;
|
|
24
|
-
return left.every((item, index) => PsDeepEqualService.isEqual(item, right[index]));
|
|
25
|
-
}
|
|
26
|
-
static isRecordEqual(left, right) {
|
|
27
|
-
const leftEntries = Object.entries(left);
|
|
28
|
-
const rightRecord = { ...right };
|
|
29
|
-
if (leftEntries.length !== Object.keys(rightRecord).length)
|
|
30
|
-
return false;
|
|
31
|
-
return leftEntries.every(([key, value]) => Object.prototype.hasOwnProperty.call(rightRecord, key) &&
|
|
32
|
-
PsDeepEqualService.isEqual(value, rightRecord[key]));
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
exports.PsDeepEqualService = PsDeepEqualService;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { PsOptimisticFailureReason } from '../interfaces/ps-optimistic-failure-reason';
|
|
2
|
-
import type { PsOptimisticOutcome } from '../interfaces/ps-optimistic-outcome';
|
|
3
|
-
export declare class PsOptimisticFetcherService {
|
|
4
|
-
static outcome<Data>(data: Data | undefined, before: Data | undefined): PsOptimisticOutcome<Data>;
|
|
5
|
-
static failureReason<Data>(outcome: PsOptimisticOutcome<Data>, isSuccess: (data: Data | undefined) => boolean): PsOptimisticFailureReason | null;
|
|
6
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PsOptimisticFetcherService = void 0;
|
|
4
|
-
class PsOptimisticFetcherService {
|
|
5
|
-
static outcome(data, before) {
|
|
6
|
-
if (data === before)
|
|
7
|
-
return { data: undefined, reason: 'no-response' };
|
|
8
|
-
return { data, reason: 'settled' };
|
|
9
|
-
}
|
|
10
|
-
static failureReason(outcome, isSuccess) {
|
|
11
|
-
if (outcome.reason !== 'settled')
|
|
12
|
-
return outcome.reason;
|
|
13
|
-
return isSuccess(outcome.data) ? null : 'rejected';
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
exports.PsOptimisticFetcherService = PsOptimisticFetcherService;
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { PsOptimisticChanges } from '../interfaces/ps-optimistic-changes';
|
|
2
|
-
export declare class PsOptimisticReleaseService {
|
|
3
|
-
static empty<Row, Change>(rows: Row[]): PsOptimisticChanges<Row, Change>;
|
|
4
|
-
static next<Row, Change>(remembered: PsOptimisticChanges<Row, Change>, rows: Row[], isActive: boolean, isHolding: boolean, apply: (rows: Row[], changes: Change[]) => Row[]): PsOptimisticChanges<Row, Change>;
|
|
5
|
-
}
|