@pallasoft/ps-core-components 2.45.0 → 2.46.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 +7 -2
- package/dist/components/ps-data-boundary.js +13 -1
- package/dist/constants/ps-data-boundary-config.d.ts +4 -0
- package/dist/constants/ps-data-boundary-config.js +7 -0
- package/dist/constants/ps-data-boundary-variant.d.ts +4 -0
- package/dist/constants/ps-data-boundary-variant.js +8 -0
- package/dist/constants/ps-optimistic-config.d.ts +1 -0
- package/dist/constants/ps-optimistic-config.js +1 -0
- package/dist/constants/ps-skeleton-config.d.ts +1 -0
- package/dist/constants/ps-skeleton-config.js +1 -0
- package/dist/esm/components/ps-data-boundary.js +14 -2
- package/dist/esm/constants/ps-data-boundary-config.js +4 -0
- package/dist/esm/constants/ps-data-boundary-variant.js +5 -0
- package/dist/esm/constants/ps-optimistic-config.js +1 -0
- package/dist/esm/constants/ps-skeleton-config.js +1 -0
- package/dist/esm/hooks/use-ps-optimistic-rows.js +24 -12
- package/dist/esm/hooks/use-ps-promise-value.js +15 -0
- package/dist/esm/hooks/use-ps-retained-promise.js +21 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/interfaces/ps-retained-promise-data.js +1 -0
- package/dist/esm/services/ps-deep-equal-service.js +31 -0
- package/dist/esm/services/ps-optimistic-release-service.js +38 -0
- package/dist/esm/services/ps-skeleton-class-service.js +11 -1
- package/dist/hooks/use-ps-optimistic-rows.js +23 -11
- package/dist/hooks/use-ps-promise-value.d.ts +1 -0
- package/dist/hooks/use-ps-promise-value.js +19 -0
- package/dist/hooks/use-ps-retained-promise.d.ts +2 -0
- package/dist/hooks/use-ps-retained-promise.js +25 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/interfaces/ps-optimistic-changes.d.ts +3 -0
- package/dist/interfaces/ps-retained-promise-data.d.ts +4 -0
- package/dist/interfaces/ps-retained-promise-data.js +2 -0
- package/dist/services/ps-deep-equal-service.d.ts +5 -0
- package/dist/services/ps-deep-equal-service.js +35 -0
- package/dist/services/ps-optimistic-release-service.d.ts +5 -0
- package/dist/services/ps-optimistic-release-service.js +42 -0
- package/dist/services/ps-skeleton-class-service.d.ts +1 -0
- package/dist/services/ps-skeleton-class-service.js +11 -1
- package/package.json +1 -1
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { type ElementType, type ReactNode } from 'react';
|
|
2
|
+
import { PsDataBoundaryVariant } from '../constants/ps-data-boundary-variant';
|
|
2
3
|
export interface PsDataBoundaryProps<Data> {
|
|
3
4
|
AwaitComponent: ElementType;
|
|
4
|
-
children: (data: Data) => ReactNode;
|
|
5
|
+
children: (data: Data, isPending: boolean) => ReactNode;
|
|
6
|
+
className?: string;
|
|
5
7
|
errorElement: ReactNode;
|
|
6
8
|
fallback: ReactNode;
|
|
7
9
|
resolve: Promise<Data>;
|
|
10
|
+
retain?: boolean;
|
|
11
|
+
retainKey?: string;
|
|
12
|
+
variant?: PsDataBoundaryVariant;
|
|
8
13
|
}
|
|
9
|
-
export declare const PsDataBoundary: <Data>({ AwaitComponent, children, errorElement, fallback, resolve, }: PsDataBoundaryProps<Data>) => import("react").JSX.Element;
|
|
14
|
+
export declare const PsDataBoundary: <Data>({ AwaitComponent, children, className, errorElement, fallback, resolve, retain, retainKey, variant, }: PsDataBoundaryProps<Data>) => import("react").JSX.Element;
|
|
@@ -3,5 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PsDataBoundary = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_1 = require("react");
|
|
6
|
-
const
|
|
6
|
+
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
7
|
+
const ps_data_boundary_config_1 = require("../constants/ps-data-boundary-config");
|
|
8
|
+
const ps_data_boundary_variant_1 = require("../constants/ps-data-boundary-variant");
|
|
9
|
+
const PsDataBoundary = ({ AwaitComponent, children, className, errorElement, fallback, resolve, retain = false, retainKey, variant = ps_data_boundary_variant_1.PsDataBoundaryVariant.Page, }) => {
|
|
10
|
+
const deferred = (0, react_1.useDeferredValue)({ key: retainKey, resolve });
|
|
11
|
+
const isRetaining = retain && deferred.key === retainKey;
|
|
12
|
+
const shown = isRetaining ? deferred.resolve : resolve;
|
|
13
|
+
const isPending = shown !== resolve;
|
|
14
|
+
const content = ((0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: fallback, children: (0, jsx_runtime_1.jsx)(AwaitComponent, { errorElement: errorElement, resolve: shown, children: (data) => children(data, isPending) }) }));
|
|
15
|
+
if (variant === ps_data_boundary_variant_1.PsDataBoundaryVariant.Page)
|
|
16
|
+
return content;
|
|
17
|
+
return ((0, jsx_runtime_1.jsx)("div", { "aria-busy": isPending, className: ps_design_system_1.PallasoftClassNameService.merge(ps_data_boundary_config_1.PsDataBoundaryConfig.sectionClassName, isPending ? ps_data_boundary_config_1.PsDataBoundaryConfig.pendingClassName : null, className), children: content }));
|
|
18
|
+
};
|
|
7
19
|
exports.PsDataBoundary = PsDataBoundary;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsDataBoundaryVariant = void 0;
|
|
4
|
+
var PsDataBoundaryVariant;
|
|
5
|
+
(function (PsDataBoundaryVariant) {
|
|
6
|
+
PsDataBoundaryVariant["Page"] = "page";
|
|
7
|
+
PsDataBoundaryVariant["Section"] = "section";
|
|
8
|
+
})(PsDataBoundaryVariant || (exports.PsDataBoundaryVariant = PsDataBoundaryVariant = {}));
|
|
@@ -10,6 +10,7 @@ export declare const PsSkeletonConfig: {
|
|
|
10
10
|
readonly importantPrefix: "!";
|
|
11
11
|
readonly widthUtilityPattern: RegExp;
|
|
12
12
|
readonly heightUtilityPattern: RegExp;
|
|
13
|
+
readonly radiusUtilityPattern: RegExp;
|
|
13
14
|
readonly compactControlHeightClassName: "h-ps-control-compact";
|
|
14
15
|
readonly tabWidthClassName: "w-24";
|
|
15
16
|
readonly tabCount: 4;
|
|
@@ -16,6 +16,7 @@ exports.PsSkeletonConfig = {
|
|
|
16
16
|
importantPrefix: '!',
|
|
17
17
|
widthUtilityPattern: /^(w|size)-/,
|
|
18
18
|
heightUtilityPattern: /^(h|size)-/,
|
|
19
|
+
radiusUtilityPattern: /^rounded(-|$)/,
|
|
19
20
|
compactControlHeightClassName: 'h-ps-control-compact',
|
|
20
21
|
tabWidthClassName: 'w-24',
|
|
21
22
|
tabCount: 4,
|
|
@@ -1,3 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Suspense } from 'react';
|
|
3
|
-
|
|
2
|
+
import { Suspense, useDeferredValue } from 'react';
|
|
3
|
+
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
4
|
+
import { PsDataBoundaryConfig } from '../constants/ps-data-boundary-config.js';
|
|
5
|
+
import { PsDataBoundaryVariant } from '../constants/ps-data-boundary-variant.js';
|
|
6
|
+
export const PsDataBoundary = ({ AwaitComponent, children, className, errorElement, fallback, resolve, retain = false, retainKey, variant = PsDataBoundaryVariant.Page, }) => {
|
|
7
|
+
const deferred = useDeferredValue({ key: retainKey, resolve });
|
|
8
|
+
const isRetaining = retain && deferred.key === retainKey;
|
|
9
|
+
const shown = isRetaining ? deferred.resolve : resolve;
|
|
10
|
+
const isPending = shown !== resolve;
|
|
11
|
+
const content = (_jsx(Suspense, { fallback: fallback, children: _jsx(AwaitComponent, { errorElement: errorElement, resolve: shown, children: (data) => children(data, isPending) }) }));
|
|
12
|
+
if (variant === PsDataBoundaryVariant.Page)
|
|
13
|
+
return content;
|
|
14
|
+
return (_jsx("div", { "aria-busy": isPending, className: PallasoftClassNameService.merge(PsDataBoundaryConfig.sectionClassName, isPending ? PsDataBoundaryConfig.pendingClassName : null, className), children: content }));
|
|
15
|
+
};
|
|
@@ -13,6 +13,7 @@ export const PsSkeletonConfig = {
|
|
|
13
13
|
importantPrefix: '!',
|
|
14
14
|
widthUtilityPattern: /^(w|size)-/,
|
|
15
15
|
heightUtilityPattern: /^(h|size)-/,
|
|
16
|
+
radiusUtilityPattern: /^rounded(-|$)/,
|
|
16
17
|
compactControlHeightClassName: 'h-ps-control-compact',
|
|
17
18
|
tabWidthClassName: 'w-24',
|
|
18
19
|
tabCount: 4,
|
|
@@ -1,17 +1,29 @@
|
|
|
1
|
-
import { useCallback, useMemo, useState } from 'react';
|
|
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';
|
|
2
4
|
export const usePsOptimisticRows = ({ apply, isHolding = false, isSaving, rows, }) => {
|
|
3
|
-
const [remembered, setRemembered] = useState(
|
|
4
|
-
base: rows,
|
|
5
|
-
changes: [],
|
|
6
|
-
});
|
|
5
|
+
const [remembered, setRemembered] = useState(() => PsOptimisticReleaseService.empty(rows));
|
|
7
6
|
const isActive = isSaving || isHolding;
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
10
|
-
setRemembered(
|
|
11
|
-
const changes =
|
|
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
12
|
const shown = useMemo(() => (changes.length === 0 ? rows : apply(rows, changes)), [apply, changes, rows]);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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]);
|
|
16
28
|
return { forgetLast, remember, rollback, rows: shown };
|
|
17
29
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
export const usePsPromiseValue = (promise, initial) => {
|
|
3
|
+
const [value, setValue] = useState(initial);
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
let isCurrent = true;
|
|
6
|
+
void promise.then((resolved) => {
|
|
7
|
+
if (isCurrent)
|
|
8
|
+
setValue(resolved);
|
|
9
|
+
}, () => undefined);
|
|
10
|
+
return () => {
|
|
11
|
+
isCurrent = false;
|
|
12
|
+
};
|
|
13
|
+
}, [promise]);
|
|
14
|
+
return value;
|
|
15
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
export const usePsRetainedPromise = (promise) => {
|
|
3
|
+
const [state, setState] = useState({
|
|
4
|
+
hasFailed: false,
|
|
5
|
+
value: null,
|
|
6
|
+
});
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
let isCurrent = true;
|
|
9
|
+
void promise.then((value) => {
|
|
10
|
+
if (isCurrent)
|
|
11
|
+
setState({ hasFailed: false, value });
|
|
12
|
+
}, () => {
|
|
13
|
+
if (isCurrent)
|
|
14
|
+
setState((previous) => ({ ...previous, hasFailed: true }));
|
|
15
|
+
});
|
|
16
|
+
return () => {
|
|
17
|
+
isCurrent = false;
|
|
18
|
+
};
|
|
19
|
+
}, [promise]);
|
|
20
|
+
return state;
|
|
21
|
+
};
|
package/dist/esm/index.js
CHANGED
|
@@ -127,6 +127,12 @@ export * from './components/ps-active-filter-chip.js';
|
|
|
127
127
|
export * from './components/ps-back-link.js';
|
|
128
128
|
export * from './components/ps-card-block-skeleton.js';
|
|
129
129
|
export * from './components/ps-data-boundary.js';
|
|
130
|
+
export * from './constants/ps-data-boundary-config.js';
|
|
131
|
+
export * from './constants/ps-data-boundary-variant.js';
|
|
132
|
+
export * from './hooks/use-ps-retained-promise.js';
|
|
133
|
+
export * from './hooks/use-ps-promise-value.js';
|
|
134
|
+
export * from './interfaces/ps-retained-promise-data.js';
|
|
135
|
+
export * from './services/ps-deep-equal-service.js';
|
|
130
136
|
export * from './components/ps-faq-answer.js';
|
|
131
137
|
export * from './components/ps-feature-grid.js';
|
|
132
138
|
export * from './components/ps-feature-card.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
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
|
+
}
|
|
@@ -10,8 +10,18 @@ export class PsSkeletonClassService {
|
|
|
10
10
|
: [];
|
|
11
11
|
const hasWidth = utilities.some((utility) => PsSkeletonConfig.widthUtilityPattern.test(utility));
|
|
12
12
|
const hasHeight = utilities.some((utility) => PsSkeletonConfig.heightUtilityPattern.test(utility));
|
|
13
|
+
const hasRadius = utilities.some((utility) => PsSkeletonConfig.radiusUtilityPattern.test(utility));
|
|
13
14
|
const size = PsSkeletonConfig.shapeSizes[shape];
|
|
14
|
-
|
|
15
|
+
const shapeClassName = hasRadius
|
|
16
|
+
? PsSkeletonClassService.withoutRadius(PsSkeletonConfig.shapeClassNames[shape])
|
|
17
|
+
: PsSkeletonConfig.shapeClassNames[shape];
|
|
18
|
+
return PallasoftClassNameService.merge(PsSkeletonConfig.baseClassName, shapeClassName, hasHeight ? null : size.height, hasWidth ? null : size.width, className);
|
|
19
|
+
}
|
|
20
|
+
static withoutRadius(className) {
|
|
21
|
+
return className
|
|
22
|
+
.split(PsSkeletonConfig.classSeparator)
|
|
23
|
+
.filter((token) => !PsSkeletonConfig.radiusUtilityPattern.test(token))
|
|
24
|
+
.join(' ');
|
|
15
25
|
}
|
|
16
26
|
static withoutImportant(token) {
|
|
17
27
|
return token.startsWith(PsSkeletonConfig.importantPrefix) ? token.slice(1) : token;
|
|
@@ -2,20 +2,32 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.usePsOptimisticRows = void 0;
|
|
4
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");
|
|
5
7
|
const usePsOptimisticRows = ({ apply, isHolding = false, isSaving, rows, }) => {
|
|
6
|
-
const [remembered, setRemembered] = (0, react_1.useState)(
|
|
7
|
-
base: rows,
|
|
8
|
-
changes: [],
|
|
9
|
-
});
|
|
8
|
+
const [remembered, setRemembered] = (0, react_1.useState)(() => ps_optimistic_release_service_1.PsOptimisticReleaseService.empty(rows));
|
|
10
9
|
const isActive = isSaving || isHolding;
|
|
11
|
-
const
|
|
12
|
-
if (
|
|
13
|
-
setRemembered(
|
|
14
|
-
const changes =
|
|
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
15
|
const shown = (0, react_1.useMemo)(() => (changes.length === 0 ? rows : apply(rows, changes)), [apply, changes, rows]);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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]);
|
|
19
31
|
return { forgetLast, remember, rollback, rows: shown };
|
|
20
32
|
};
|
|
21
33
|
exports.usePsOptimisticRows = usePsOptimisticRows;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const usePsPromiseValue: <Value>(promise: Promise<Value>, initial: Value) => Value;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePsPromiseValue = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const usePsPromiseValue = (promise, initial) => {
|
|
6
|
+
const [value, setValue] = (0, react_1.useState)(initial);
|
|
7
|
+
(0, react_1.useEffect)(() => {
|
|
8
|
+
let isCurrent = true;
|
|
9
|
+
void promise.then((resolved) => {
|
|
10
|
+
if (isCurrent)
|
|
11
|
+
setValue(resolved);
|
|
12
|
+
}, () => undefined);
|
|
13
|
+
return () => {
|
|
14
|
+
isCurrent = false;
|
|
15
|
+
};
|
|
16
|
+
}, [promise]);
|
|
17
|
+
return value;
|
|
18
|
+
};
|
|
19
|
+
exports.usePsPromiseValue = usePsPromiseValue;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePsRetainedPromise = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const usePsRetainedPromise = (promise) => {
|
|
6
|
+
const [state, setState] = (0, react_1.useState)({
|
|
7
|
+
hasFailed: false,
|
|
8
|
+
value: null,
|
|
9
|
+
});
|
|
10
|
+
(0, react_1.useEffect)(() => {
|
|
11
|
+
let isCurrent = true;
|
|
12
|
+
void promise.then((value) => {
|
|
13
|
+
if (isCurrent)
|
|
14
|
+
setState({ hasFailed: false, value });
|
|
15
|
+
}, () => {
|
|
16
|
+
if (isCurrent)
|
|
17
|
+
setState((previous) => ({ ...previous, hasFailed: true }));
|
|
18
|
+
});
|
|
19
|
+
return () => {
|
|
20
|
+
isCurrent = false;
|
|
21
|
+
};
|
|
22
|
+
}, [promise]);
|
|
23
|
+
return state;
|
|
24
|
+
};
|
|
25
|
+
exports.usePsRetainedPromise = usePsRetainedPromise;
|
package/dist/index.d.ts
CHANGED
|
@@ -127,6 +127,12 @@ export * from './components/ps-active-filter-chip';
|
|
|
127
127
|
export * from './components/ps-back-link';
|
|
128
128
|
export * from './components/ps-card-block-skeleton';
|
|
129
129
|
export * from './components/ps-data-boundary';
|
|
130
|
+
export * from './constants/ps-data-boundary-config';
|
|
131
|
+
export * from './constants/ps-data-boundary-variant';
|
|
132
|
+
export * from './hooks/use-ps-retained-promise';
|
|
133
|
+
export * from './hooks/use-ps-promise-value';
|
|
134
|
+
export * from './interfaces/ps-retained-promise-data';
|
|
135
|
+
export * from './services/ps-deep-equal-service';
|
|
130
136
|
export * from './components/ps-faq-answer';
|
|
131
137
|
export * from './components/ps-feature-grid';
|
|
132
138
|
export * from './components/ps-feature-card';
|
package/dist/index.js
CHANGED
|
@@ -143,6 +143,12 @@ __exportStar(require("./components/ps-active-filter-chip"), exports);
|
|
|
143
143
|
__exportStar(require("./components/ps-back-link"), exports);
|
|
144
144
|
__exportStar(require("./components/ps-card-block-skeleton"), exports);
|
|
145
145
|
__exportStar(require("./components/ps-data-boundary"), exports);
|
|
146
|
+
__exportStar(require("./constants/ps-data-boundary-config"), exports);
|
|
147
|
+
__exportStar(require("./constants/ps-data-boundary-variant"), exports);
|
|
148
|
+
__exportStar(require("./hooks/use-ps-retained-promise"), exports);
|
|
149
|
+
__exportStar(require("./hooks/use-ps-promise-value"), exports);
|
|
150
|
+
__exportStar(require("./interfaces/ps-retained-promise-data"), exports);
|
|
151
|
+
__exportStar(require("./services/ps-deep-equal-service"), exports);
|
|
146
152
|
__exportStar(require("./components/ps-faq-answer"), exports);
|
|
147
153
|
__exportStar(require("./components/ps-feature-grid"), exports);
|
|
148
154
|
__exportStar(require("./components/ps-feature-card"), exports);
|
|
@@ -0,0 +1,35 @@
|
|
|
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;
|
|
@@ -0,0 +1,5 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsOptimisticReleaseService = void 0;
|
|
4
|
+
const ps_deep_equal_service_1 = require("./ps-deep-equal-service");
|
|
5
|
+
class PsOptimisticReleaseService {
|
|
6
|
+
static empty(rows) {
|
|
7
|
+
return { base: rows, changes: [], drainBase: null, wasActive: false, wasHolding: false };
|
|
8
|
+
}
|
|
9
|
+
static next(remembered, rows, isActive, isHolding, apply) {
|
|
10
|
+
if (isActive) {
|
|
11
|
+
const wasHolding = remembered.wasHolding || isHolding;
|
|
12
|
+
if (remembered.wasActive &&
|
|
13
|
+
remembered.drainBase === null &&
|
|
14
|
+
remembered.wasHolding === wasHolding)
|
|
15
|
+
return remembered;
|
|
16
|
+
return { ...remembered, drainBase: null, wasActive: true, wasHolding };
|
|
17
|
+
}
|
|
18
|
+
if (remembered.changes.length === 0) {
|
|
19
|
+
if (remembered.base === rows && !remembered.wasActive)
|
|
20
|
+
return remembered;
|
|
21
|
+
return PsOptimisticReleaseService.empty(rows);
|
|
22
|
+
}
|
|
23
|
+
if (remembered.wasActive) {
|
|
24
|
+
if (remembered.wasHolding && rows !== remembered.base) {
|
|
25
|
+
return PsOptimisticReleaseService.empty(rows);
|
|
26
|
+
}
|
|
27
|
+
if (!ps_deep_equal_service_1.PsDeepEqualService.isEqual(rows, remembered.base)) {
|
|
28
|
+
return PsOptimisticReleaseService.empty(rows);
|
|
29
|
+
}
|
|
30
|
+
return { ...remembered, drainBase: rows, wasActive: false, wasHolding: false };
|
|
31
|
+
}
|
|
32
|
+
const reference = remembered.drainBase === null ? remembered.base : remembered.drainBase;
|
|
33
|
+
if (!ps_deep_equal_service_1.PsDeepEqualService.isEqual(rows, reference)) {
|
|
34
|
+
return PsOptimisticReleaseService.empty(rows);
|
|
35
|
+
}
|
|
36
|
+
if (ps_deep_equal_service_1.PsDeepEqualService.isEqual(apply(rows, remembered.changes), rows)) {
|
|
37
|
+
return PsOptimisticReleaseService.empty(rows);
|
|
38
|
+
}
|
|
39
|
+
return remembered;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.PsOptimisticReleaseService = PsOptimisticReleaseService;
|
|
@@ -13,8 +13,18 @@ class PsSkeletonClassService {
|
|
|
13
13
|
: [];
|
|
14
14
|
const hasWidth = utilities.some((utility) => ps_skeleton_config_1.PsSkeletonConfig.widthUtilityPattern.test(utility));
|
|
15
15
|
const hasHeight = utilities.some((utility) => ps_skeleton_config_1.PsSkeletonConfig.heightUtilityPattern.test(utility));
|
|
16
|
+
const hasRadius = utilities.some((utility) => ps_skeleton_config_1.PsSkeletonConfig.radiusUtilityPattern.test(utility));
|
|
16
17
|
const size = ps_skeleton_config_1.PsSkeletonConfig.shapeSizes[shape];
|
|
17
|
-
|
|
18
|
+
const shapeClassName = hasRadius
|
|
19
|
+
? PsSkeletonClassService.withoutRadius(ps_skeleton_config_1.PsSkeletonConfig.shapeClassNames[shape])
|
|
20
|
+
: ps_skeleton_config_1.PsSkeletonConfig.shapeClassNames[shape];
|
|
21
|
+
return ps_design_system_1.PallasoftClassNameService.merge(ps_skeleton_config_1.PsSkeletonConfig.baseClassName, shapeClassName, hasHeight ? null : size.height, hasWidth ? null : size.width, className);
|
|
22
|
+
}
|
|
23
|
+
static withoutRadius(className) {
|
|
24
|
+
return className
|
|
25
|
+
.split(ps_skeleton_config_1.PsSkeletonConfig.classSeparator)
|
|
26
|
+
.filter((token) => !ps_skeleton_config_1.PsSkeletonConfig.radiusUtilityPattern.test(token))
|
|
27
|
+
.join(' ');
|
|
18
28
|
}
|
|
19
29
|
static withoutImportant(token) {
|
|
20
30
|
return token.startsWith(ps_skeleton_config_1.PsSkeletonConfig.importantPrefix) ? token.slice(1) : token;
|