@pallasoft/ps-core-components 2.46.0 → 2.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/components/ps-data-boundary.d.ts +2 -1
  2. package/dist/components/ps-data-boundary.js +2 -2
  3. package/dist/components/ps-empty-value.d.ts +5 -0
  4. package/dist/components/ps-empty-value.js +8 -0
  5. package/dist/components/ps-image.d.ts +14 -0
  6. package/dist/components/ps-image.js +29 -0
  7. package/dist/components/ps-signed-value.d.ts +6 -0
  8. package/dist/components/ps-signed-value.js +9 -0
  9. package/dist/constants/ps-empty-value-config.d.ts +4 -0
  10. package/dist/constants/ps-empty-value-config.js +7 -0
  11. package/dist/constants/ps-image-config.d.ts +7 -0
  12. package/dist/constants/ps-image-config.js +10 -0
  13. package/dist/constants/ps-image-fit.d.ts +7 -0
  14. package/dist/constants/ps-image-fit.js +11 -0
  15. package/dist/constants/ps-image-status.d.ts +5 -0
  16. package/dist/constants/ps-image-status.js +9 -0
  17. package/dist/constants/ps-signed-value-classes.d.ts +2 -0
  18. package/dist/constants/ps-signed-value-classes.js +9 -0
  19. package/dist/esm/components/ps-data-boundary.js +2 -2
  20. package/dist/esm/components/ps-empty-value.js +4 -0
  21. package/dist/esm/components/ps-image.js +25 -0
  22. package/dist/esm/components/ps-signed-value.js +5 -0
  23. package/dist/esm/constants/ps-empty-value-config.js +4 -0
  24. package/dist/esm/constants/ps-image-config.js +7 -0
  25. package/dist/esm/constants/ps-image-fit.js +8 -0
  26. package/dist/esm/constants/ps-image-status.js +6 -0
  27. package/dist/esm/constants/ps-signed-value-classes.js +6 -0
  28. package/dist/esm/hooks/use-ps-cursor-pages.js +82 -0
  29. package/dist/esm/hooks/use-ps-draft-state.js +18 -0
  30. package/dist/esm/hooks/use-ps-optimistic-rows.js +2 -1
  31. package/dist/esm/hooks/use-ps-remote-read.js +31 -0
  32. package/dist/esm/hooks/use-ps-text-param.js +16 -0
  33. package/dist/esm/index.js +30 -0
  34. package/dist/esm/interfaces/ps-cursor-pages-input.js +1 -0
  35. package/dist/esm/interfaces/ps-cursor-pages-result.js +1 -0
  36. package/dist/esm/interfaces/ps-draft-state-options.js +1 -0
  37. package/dist/esm/interfaces/ps-draft-state-result.js +1 -0
  38. package/dist/esm/interfaces/ps-remote-read-input.js +1 -0
  39. package/dist/esm/interfaces/ps-remote-read-result.js +1 -0
  40. package/dist/esm/models/ps-cursor-pages-state.js +1 -0
  41. package/dist/esm/models/ps-draft-state-data.js +1 -0
  42. package/dist/esm/models/ps-image-state-data.js +1 -0
  43. package/dist/esm/models/ps-remote-read-state.js +1 -0
  44. package/dist/esm/models/ps-remote-settled-data.js +1 -0
  45. package/dist/esm/models/ps-text-param-data.js +1 -0
  46. package/dist/esm/models/ps-text-param-options.js +1 -0
  47. package/dist/esm/services/ps-cursor-pages-service.js +14 -0
  48. package/dist/esm/services/ps-draft-state-service.js +11 -0
  49. package/dist/esm/services/ps-image-service.js +11 -0
  50. package/dist/esm/services/ps-optimistic-release-service.js +11 -5
  51. package/dist/esm/services/ps-remote-read-service.js +19 -0
  52. package/dist/esm/services/ps-signed-value-service.js +13 -0
  53. package/dist/hooks/use-ps-cursor-pages.d.ts +3 -0
  54. package/dist/hooks/use-ps-cursor-pages.js +86 -0
  55. package/dist/hooks/use-ps-draft-state.d.ts +3 -0
  56. package/dist/hooks/use-ps-draft-state.js +22 -0
  57. package/dist/hooks/use-ps-optimistic-rows.js +2 -1
  58. package/dist/hooks/use-ps-remote-read.d.ts +3 -0
  59. package/dist/hooks/use-ps-remote-read.js +35 -0
  60. package/dist/hooks/use-ps-text-param.d.ts +4 -0
  61. package/dist/hooks/use-ps-text-param.js +20 -0
  62. package/dist/index.d.ts +30 -0
  63. package/dist/index.js +30 -0
  64. package/dist/interfaces/ps-cursor-pages-input.d.ts +8 -0
  65. package/dist/interfaces/ps-cursor-pages-input.js +2 -0
  66. package/dist/interfaces/ps-cursor-pages-result.d.ts +7 -0
  67. package/dist/interfaces/ps-cursor-pages-result.js +2 -0
  68. package/dist/interfaces/ps-draft-state-options.d.ts +3 -0
  69. package/dist/interfaces/ps-draft-state-options.js +2 -0
  70. package/dist/interfaces/ps-draft-state-result.d.ts +7 -0
  71. package/dist/interfaces/ps-draft-state-result.js +2 -0
  72. package/dist/interfaces/ps-optimistic-changes.d.ts +1 -0
  73. package/dist/interfaces/ps-remote-read-input.d.ts +5 -0
  74. package/dist/interfaces/ps-remote-read-input.js +2 -0
  75. package/dist/interfaces/ps-remote-read-result.d.ts +5 -0
  76. package/dist/interfaces/ps-remote-read-result.js +2 -0
  77. package/dist/models/ps-cursor-pages-state.d.ts +8 -0
  78. package/dist/models/ps-cursor-pages-state.js +2 -0
  79. package/dist/models/ps-draft-state-data.d.ts +4 -0
  80. package/dist/models/ps-draft-state-data.js +2 -0
  81. package/dist/models/ps-image-state-data.d.ts +5 -0
  82. package/dist/models/ps-image-state-data.js +2 -0
  83. package/dist/models/ps-remote-read-state.d.ts +6 -0
  84. package/dist/models/ps-remote-read-state.js +2 -0
  85. package/dist/models/ps-remote-settled-data.d.ts +4 -0
  86. package/dist/models/ps-remote-settled-data.js +2 -0
  87. package/dist/models/ps-text-param-data.d.ts +5 -0
  88. package/dist/models/ps-text-param-data.js +2 -0
  89. package/dist/models/ps-text-param-options.d.ts +5 -0
  90. package/dist/models/ps-text-param-options.js +2 -0
  91. package/dist/services/ps-cursor-pages-service.d.ts +6 -0
  92. package/dist/services/ps-cursor-pages-service.js +18 -0
  93. package/dist/services/ps-draft-state-service.d.ts +6 -0
  94. package/dist/services/ps-draft-state-service.js +15 -0
  95. package/dist/services/ps-image-service.d.ts +6 -0
  96. package/dist/services/ps-image-service.js +15 -0
  97. package/dist/services/ps-optimistic-release-service.d.ts +1 -1
  98. package/dist/services/ps-optimistic-release-service.js +11 -5
  99. package/dist/services/ps-remote-read-service.d.ts +8 -0
  100. package/dist/services/ps-remote-read-service.js +23 -0
  101. package/dist/services/ps-signed-value-service.d.ts +5 -0
  102. package/dist/services/ps-signed-value-service.js +17 -0
  103. package/package.json +2 -2
@@ -4,6 +4,7 @@ export interface PsDataBoundaryProps<Data> {
4
4
  AwaitComponent: ElementType;
5
5
  children: (data: Data, isPending: boolean) => ReactNode;
6
6
  className?: string;
7
+ dimWhenPending?: boolean;
7
8
  errorElement: ReactNode;
8
9
  fallback: ReactNode;
9
10
  resolve: Promise<Data>;
@@ -11,4 +12,4 @@ export interface PsDataBoundaryProps<Data> {
11
12
  retainKey?: string;
12
13
  variant?: PsDataBoundaryVariant;
13
14
  }
14
- export declare const PsDataBoundary: <Data>({ AwaitComponent, children, className, errorElement, fallback, resolve, retain, retainKey, variant, }: PsDataBoundaryProps<Data>) => import("react").JSX.Element;
15
+ export declare const PsDataBoundary: <Data>({ AwaitComponent, children, className, dimWhenPending, errorElement, fallback, resolve, retain, retainKey, variant, }: PsDataBoundaryProps<Data>) => import("react").JSX.Element;
@@ -6,7 +6,7 @@ const react_1 = require("react");
6
6
  const ps_design_system_1 = require("@pallasoft/ps-design-system");
7
7
  const ps_data_boundary_config_1 = require("../constants/ps-data-boundary-config");
8
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, }) => {
9
+ const PsDataBoundary = ({ AwaitComponent, children, className, dimWhenPending = false, errorElement, fallback, resolve, retain = false, retainKey, variant = ps_data_boundary_variant_1.PsDataBoundaryVariant.Page, }) => {
10
10
  const deferred = (0, react_1.useDeferredValue)({ key: retainKey, resolve });
11
11
  const isRetaining = retain && deferred.key === retainKey;
12
12
  const shown = isRetaining ? deferred.resolve : resolve;
@@ -14,6 +14,6 @@ const PsDataBoundary = ({ AwaitComponent, children, className, errorElement, fal
14
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
15
  if (variant === ps_data_boundary_variant_1.PsDataBoundaryVariant.Page)
16
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 }));
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 && dimWhenPending ? ps_data_boundary_config_1.PsDataBoundaryConfig.pendingClassName : null, className), children: content }));
18
18
  };
19
19
  exports.PsDataBoundary = PsDataBoundary;
@@ -0,0 +1,5 @@
1
+ export interface PsEmptyValueProps {
2
+ className?: string;
3
+ label: string;
4
+ }
5
+ export declare const PsEmptyValue: ({ className, label }: PsEmptyValueProps) => import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsEmptyValue = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const ps_empty_value_config_1 = require("../constants/ps-empty-value-config");
7
+ const PsEmptyValue = ({ className, label }) => ((0, jsx_runtime_1.jsxs)("span", { className: ps_design_system_1.PallasoftClassNameService.merge(ps_empty_value_config_1.PsEmptyValueConfig.className, className), children: [(0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', children: ps_empty_value_config_1.PsEmptyValueConfig.symbol }), (0, jsx_runtime_1.jsx)("span", { className: 'sr-only', children: label })] }));
8
+ exports.PsEmptyValue = PsEmptyValue;
@@ -0,0 +1,14 @@
1
+ import { type ImgHTMLAttributes, type ReactNode } from 'react';
2
+ import type { PsImageFit } from '../constants/ps-image-fit';
3
+ export interface PsImageProps {
4
+ alt: string;
5
+ className?: string;
6
+ errorFallback?: ReactNode;
7
+ fit?: PsImageFit;
8
+ height?: number | string;
9
+ loading?: ImgHTMLAttributes<HTMLImageElement>['loading'];
10
+ radius?: string;
11
+ src: string;
12
+ width?: number | string;
13
+ }
14
+ export declare const PsImage: ({ alt, className, errorFallback, fit, height, loading, radius, src, width, }: PsImageProps) => import("react").JSX.Element | null;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsImage = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
7
+ const ps_image_config_1 = require("../constants/ps-image-config");
8
+ const ps_image_status_1 = require("../constants/ps-image-status");
9
+ const ps_skeleton_shape_1 = require("../constants/ps-skeleton-shape");
10
+ const use_ps_browser_layout_effect_1 = require("../hooks/use-ps-browser-layout-effect");
11
+ const ps_image_service_1 = require("../services/ps-image-service");
12
+ const ps_skeleton_1 = require("./ps-skeleton");
13
+ const PsImage = ({ alt, className, errorFallback, fit, height, loading, radius, src, width, }) => {
14
+ const element = (0, react_1.useRef)(null);
15
+ const [state, setState] = (0, react_1.useState)({ src, status: ps_image_status_1.PsImageStatus.Loading });
16
+ const status = ps_image_service_1.PsImageService.resolve(state, src);
17
+ (0, use_ps_browser_layout_effect_1.usePsBrowserLayoutEffect)(() => {
18
+ if (element.current === null)
19
+ return;
20
+ const found = ps_image_service_1.PsImageService.statusOf(element.current);
21
+ if (found !== ps_image_status_1.PsImageStatus.Loading)
22
+ setState({ src, status: found });
23
+ }, [src]);
24
+ if (status === ps_image_status_1.PsImageStatus.Error)
25
+ return errorFallback === undefined ? null : (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: errorFallback });
26
+ const isLoaded = status === ps_image_status_1.PsImageStatus.Loaded;
27
+ return ((0, jsx_runtime_1.jsxs)("span", { className: ps_image_config_1.PsImageConfig.rootClassName, style: { borderRadius: radius }, children: [isLoaded ? null : ((0, jsx_runtime_1.jsx)(ps_skeleton_1.PsSkeleton, { className: ps_image_config_1.PsImageConfig.skeletonClassName, shape: ps_skeleton_shape_1.PsSkeletonShape.Block })), (0, jsx_runtime_1.jsx)("img", { alt: alt, className: ps_design_system_1.PallasoftClassNameService.merge(ps_image_config_1.PsImageConfig.imageClassName, isLoaded ? ps_image_config_1.PsImageConfig.loadedClassName : ps_image_config_1.PsImageConfig.hiddenClassName, className), decoding: 'async', height: height, loading: loading, onError: () => setState({ src, status: ps_image_status_1.PsImageStatus.Error }), onLoad: () => setState({ src, status: ps_image_status_1.PsImageStatus.Loaded }), ref: element, src: src, style: fit === undefined ? undefined : { objectFit: fit }, width: width })] }));
28
+ };
29
+ exports.PsImage = PsImage;
@@ -0,0 +1,6 @@
1
+ export interface PsSignedValueProps {
2
+ className?: string;
3
+ format?: (value: string) => string;
4
+ value: string;
5
+ }
6
+ export declare const PsSignedValue: ({ className, format, value }: PsSignedValueProps) => import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsSignedValue = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const ps_signed_value_classes_1 = require("../constants/ps-signed-value-classes");
7
+ const ps_signed_value_service_1 = require("../services/ps-signed-value-service");
8
+ const PsSignedValue = ({ className, format, value }) => ((0, jsx_runtime_1.jsx)("span", { className: ps_design_system_1.PallasoftClassNameService.merge(ps_signed_value_classes_1.PsSignedValueClasses[ps_signed_value_service_1.PsSignedValueService.direction(value)], className), children: ps_signed_value_service_1.PsSignedValueService.text(value, format) }));
9
+ exports.PsSignedValue = PsSignedValue;
@@ -0,0 +1,4 @@
1
+ export declare const PsEmptyValueConfig: {
2
+ readonly className: "text-ink/40";
3
+ readonly symbol: "—";
4
+ };
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsEmptyValueConfig = void 0;
4
+ exports.PsEmptyValueConfig = {
5
+ className: 'text-ink/40',
6
+ symbol: '—',
7
+ };
@@ -0,0 +1,7 @@
1
+ export declare const PsImageConfig: {
2
+ readonly hiddenClassName: "opacity-0";
3
+ readonly imageClassName: "col-start-1 row-start-1";
4
+ readonly loadedClassName: "opacity-100 animate-ps-fade-in motion-reduce:animate-none";
5
+ readonly rootClassName: "inline-grid shrink-0 max-w-full overflow-hidden align-middle";
6
+ readonly skeletonClassName: "col-start-1 row-start-1 h-full w-full rounded-[inherit]";
7
+ };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsImageConfig = void 0;
4
+ exports.PsImageConfig = {
5
+ hiddenClassName: 'opacity-0',
6
+ imageClassName: 'col-start-1 row-start-1',
7
+ loadedClassName: 'opacity-100 animate-ps-fade-in motion-reduce:animate-none',
8
+ rootClassName: 'inline-grid shrink-0 max-w-full overflow-hidden align-middle',
9
+ skeletonClassName: 'col-start-1 row-start-1 h-full w-full rounded-[inherit]',
10
+ };
@@ -0,0 +1,7 @@
1
+ export declare enum PsImageFit {
2
+ Contain = "contain",
3
+ Cover = "cover",
4
+ Fill = "fill",
5
+ None = "none",
6
+ ScaleDown = "scale-down"
7
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsImageFit = void 0;
4
+ var PsImageFit;
5
+ (function (PsImageFit) {
6
+ PsImageFit["Contain"] = "contain";
7
+ PsImageFit["Cover"] = "cover";
8
+ PsImageFit["Fill"] = "fill";
9
+ PsImageFit["None"] = "none";
10
+ PsImageFit["ScaleDown"] = "scale-down";
11
+ })(PsImageFit || (exports.PsImageFit = PsImageFit = {}));
@@ -0,0 +1,5 @@
1
+ export declare enum PsImageStatus {
2
+ Error = "error",
3
+ Loaded = "loaded",
4
+ Loading = "loading"
5
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsImageStatus = void 0;
4
+ var PsImageStatus;
5
+ (function (PsImageStatus) {
6
+ PsImageStatus["Error"] = "error";
7
+ PsImageStatus["Loaded"] = "loaded";
8
+ PsImageStatus["Loading"] = "loading";
9
+ })(PsImageStatus || (exports.PsImageStatus = PsImageStatus = {}));
@@ -0,0 +1,2 @@
1
+ import { AmountDirection } from '@pallasoft/ps-utilities';
2
+ export declare const PsSignedValueClasses: Record<AmountDirection, string>;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsSignedValueClasses = void 0;
4
+ const ps_utilities_1 = require("@pallasoft/ps-utilities");
5
+ exports.PsSignedValueClasses = {
6
+ [ps_utilities_1.AmountDirection.In]: 'tabular-nums text-success',
7
+ [ps_utilities_1.AmountDirection.Neutral]: 'tabular-nums text-ink',
8
+ [ps_utilities_1.AmountDirection.Out]: 'tabular-nums text-danger',
9
+ };
@@ -3,7 +3,7 @@ import { Suspense, useDeferredValue } from 'react';
3
3
  import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
4
4
  import { PsDataBoundaryConfig } from '../constants/ps-data-boundary-config.js';
5
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, }) => {
6
+ export const PsDataBoundary = ({ AwaitComponent, children, className, dimWhenPending = false, errorElement, fallback, resolve, retain = false, retainKey, variant = PsDataBoundaryVariant.Page, }) => {
7
7
  const deferred = useDeferredValue({ key: retainKey, resolve });
8
8
  const isRetaining = retain && deferred.key === retainKey;
9
9
  const shown = isRetaining ? deferred.resolve : resolve;
@@ -11,5 +11,5 @@ export const PsDataBoundary = ({ AwaitComponent, children, className, errorEleme
11
11
  const content = (_jsx(Suspense, { fallback: fallback, children: _jsx(AwaitComponent, { errorElement: errorElement, resolve: shown, children: (data) => children(data, isPending) }) }));
12
12
  if (variant === PsDataBoundaryVariant.Page)
13
13
  return content;
14
- return (_jsx("div", { "aria-busy": isPending, className: PallasoftClassNameService.merge(PsDataBoundaryConfig.sectionClassName, isPending ? PsDataBoundaryConfig.pendingClassName : null, className), children: content }));
14
+ return (_jsx("div", { "aria-busy": isPending, className: PallasoftClassNameService.merge(PsDataBoundaryConfig.sectionClassName, isPending && dimWhenPending ? PsDataBoundaryConfig.pendingClassName : null, className), children: content }));
15
15
  };
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
3
+ import { PsEmptyValueConfig } from '../constants/ps-empty-value-config.js';
4
+ export const PsEmptyValue = ({ className, label }) => (_jsxs("span", { className: PallasoftClassNameService.merge(PsEmptyValueConfig.className, className), children: [_jsx("span", { "aria-hidden": 'true', children: PsEmptyValueConfig.symbol }), _jsx("span", { className: 'sr-only', children: label })] }));
@@ -0,0 +1,25 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useState } from 'react';
3
+ import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
4
+ import { PsImageConfig } from '../constants/ps-image-config.js';
5
+ import { PsImageStatus } from '../constants/ps-image-status.js';
6
+ import { PsSkeletonShape } from '../constants/ps-skeleton-shape.js';
7
+ import { usePsBrowserLayoutEffect } from '../hooks/use-ps-browser-layout-effect.js';
8
+ import { PsImageService } from '../services/ps-image-service.js';
9
+ import { PsSkeleton } from './ps-skeleton.js';
10
+ export const PsImage = ({ alt, className, errorFallback, fit, height, loading, radius, src, width, }) => {
11
+ const element = useRef(null);
12
+ const [state, setState] = useState({ src, status: PsImageStatus.Loading });
13
+ const status = PsImageService.resolve(state, src);
14
+ usePsBrowserLayoutEffect(() => {
15
+ if (element.current === null)
16
+ return;
17
+ const found = PsImageService.statusOf(element.current);
18
+ if (found !== PsImageStatus.Loading)
19
+ setState({ src, status: found });
20
+ }, [src]);
21
+ if (status === PsImageStatus.Error)
22
+ return errorFallback === undefined ? null : _jsx(_Fragment, { children: errorFallback });
23
+ const isLoaded = status === PsImageStatus.Loaded;
24
+ return (_jsxs("span", { className: PsImageConfig.rootClassName, style: { borderRadius: radius }, children: [isLoaded ? null : (_jsx(PsSkeleton, { className: PsImageConfig.skeletonClassName, shape: PsSkeletonShape.Block })), _jsx("img", { alt: alt, className: PallasoftClassNameService.merge(PsImageConfig.imageClassName, isLoaded ? PsImageConfig.loadedClassName : PsImageConfig.hiddenClassName, className), decoding: 'async', height: height, loading: loading, onError: () => setState({ src, status: PsImageStatus.Error }), onLoad: () => setState({ src, status: PsImageStatus.Loaded }), ref: element, src: src, style: fit === undefined ? undefined : { objectFit: fit }, width: width })] }));
25
+ };
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
3
+ import { PsSignedValueClasses } from '../constants/ps-signed-value-classes.js';
4
+ import { PsSignedValueService } from '../services/ps-signed-value-service.js';
5
+ export const PsSignedValue = ({ className, format, value }) => (_jsx("span", { className: PallasoftClassNameService.merge(PsSignedValueClasses[PsSignedValueService.direction(value)], className), children: PsSignedValueService.text(value, format) }));
@@ -0,0 +1,4 @@
1
+ export const PsEmptyValueConfig = {
2
+ className: 'text-ink/40',
3
+ symbol: '—',
4
+ };
@@ -0,0 +1,7 @@
1
+ export const PsImageConfig = {
2
+ hiddenClassName: 'opacity-0',
3
+ imageClassName: 'col-start-1 row-start-1',
4
+ loadedClassName: 'opacity-100 animate-ps-fade-in motion-reduce:animate-none',
5
+ rootClassName: 'inline-grid shrink-0 max-w-full overflow-hidden align-middle',
6
+ skeletonClassName: 'col-start-1 row-start-1 h-full w-full rounded-[inherit]',
7
+ };
@@ -0,0 +1,8 @@
1
+ export var PsImageFit;
2
+ (function (PsImageFit) {
3
+ PsImageFit["Contain"] = "contain";
4
+ PsImageFit["Cover"] = "cover";
5
+ PsImageFit["Fill"] = "fill";
6
+ PsImageFit["None"] = "none";
7
+ PsImageFit["ScaleDown"] = "scale-down";
8
+ })(PsImageFit || (PsImageFit = {}));
@@ -0,0 +1,6 @@
1
+ export var PsImageStatus;
2
+ (function (PsImageStatus) {
3
+ PsImageStatus["Error"] = "error";
4
+ PsImageStatus["Loaded"] = "loaded";
5
+ PsImageStatus["Loading"] = "loading";
6
+ })(PsImageStatus || (PsImageStatus = {}));
@@ -0,0 +1,6 @@
1
+ import { AmountDirection } from '@pallasoft/ps-utilities';
2
+ export const PsSignedValueClasses = {
3
+ [AmountDirection.In]: 'tabular-nums text-success',
4
+ [AmountDirection.Neutral]: 'tabular-nums text-ink',
5
+ [AmountDirection.Out]: 'tabular-nums text-danger',
6
+ };
@@ -0,0 +1,82 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { PsCursorPagesService } from '../services/ps-cursor-pages-service.js';
3
+ import { PsRemoteReadService } from '../services/ps-remote-read-service.js';
4
+ export const usePsCursorPages = (input) => {
5
+ const { reloadKey } = input;
6
+ const firstUrl = input.url(null);
7
+ const [state, setState] = useState(() => PsCursorPagesService.initialState());
8
+ const inputRef = useRef(input);
9
+ const stateRef = useRef(state);
10
+ const inflight = useRef(null);
11
+ inputRef.current = input;
12
+ stateRef.current = state;
13
+ useEffect(() => {
14
+ setState((current) => (current.isLoadingMore ? { ...current, isLoadingMore: false } : current));
15
+ if (firstUrl === null)
16
+ return;
17
+ const controller = new AbortController();
18
+ inflight.current = controller;
19
+ PsRemoteReadService.read(firstUrl, inputRef.current.parse, controller.signal).then((page) => {
20
+ if (controller.signal.aborted)
21
+ return;
22
+ inflight.current = null;
23
+ setState({
24
+ error: null,
25
+ hasMore: PsCursorPagesService.hasMore(page.length, inputRef.current.pageSize),
26
+ isLoadingMore: false,
27
+ rows: page,
28
+ settled: { reloadKey, url: firstUrl },
29
+ });
30
+ }, (failure) => {
31
+ if (controller.signal.aborted)
32
+ return;
33
+ inflight.current = null;
34
+ setState({
35
+ error: failure,
36
+ hasMore: false,
37
+ isLoadingMore: false,
38
+ rows: [],
39
+ settled: { reloadKey, url: firstUrl },
40
+ });
41
+ });
42
+ return () => {
43
+ controller.abort();
44
+ inflight.current = null;
45
+ };
46
+ }, [firstUrl, reloadKey]);
47
+ const loadMore = useCallback(() => {
48
+ const current = stateRef.current;
49
+ const latest = inputRef.current;
50
+ if (!current.hasMore || inflight.current !== null)
51
+ return;
52
+ const nextUrl = latest.url(latest.cursorOf(current.rows));
53
+ if (nextUrl === null)
54
+ return;
55
+ const controller = new AbortController();
56
+ inflight.current = controller;
57
+ setState((existing) => ({ ...existing, error: null, isLoadingMore: true }));
58
+ PsRemoteReadService.read(nextUrl, latest.parse, controller.signal).then((page) => {
59
+ if (controller.signal.aborted)
60
+ return;
61
+ inflight.current = null;
62
+ setState((existing) => ({
63
+ ...existing,
64
+ hasMore: PsCursorPagesService.hasMore(page.length, inputRef.current.pageSize),
65
+ isLoadingMore: false,
66
+ rows: PsCursorPagesService.append(existing.rows, page, inputRef.current.keyOf),
67
+ }));
68
+ }, (failure) => {
69
+ if (controller.signal.aborted)
70
+ return;
71
+ inflight.current = null;
72
+ setState((existing) => ({ ...existing, error: failure, isLoadingMore: false }));
73
+ });
74
+ }, []);
75
+ return {
76
+ error: state.error,
77
+ hasMore: state.hasMore,
78
+ isLoading: PsRemoteReadService.isPending(state.settled, firstUrl, reloadKey) || state.isLoadingMore,
79
+ loadMore,
80
+ rows: state.rows,
81
+ };
82
+ };
@@ -0,0 +1,18 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { PsDraftStateService } from '../services/ps-draft-state-service.js';
3
+ export const usePsDraftState = (initial, options) => {
4
+ const [state, setState] = useState(() => PsDraftStateService.fresh(initial));
5
+ const isCurrent = PsDraftStateService.isEqual(initial, state.baseline, options);
6
+ if (!isCurrent)
7
+ setState(PsDraftStateService.fresh(initial));
8
+ const setValue = useCallback((next) => setState((current) => ({ ...current, value: next })), []);
9
+ const update = useCallback((updater) => setState((current) => ({ ...current, value: updater(current.value) })), []);
10
+ const reset = useCallback(() => setState((current) => ({ ...current, value: structuredClone(current.baseline) })), []);
11
+ return {
12
+ isDirty: !PsDraftStateService.isEqual(state.value, state.baseline, options),
13
+ reset,
14
+ setValue,
15
+ update,
16
+ value: state.value,
17
+ };
18
+ };
@@ -4,7 +4,7 @@ import { PsOptimisticReleaseService } from '../services/ps-optimistic-release-se
4
4
  export const usePsOptimisticRows = ({ apply, isHolding = false, isSaving, rows, }) => {
5
5
  const [remembered, setRemembered] = useState(() => PsOptimisticReleaseService.empty(rows));
6
6
  const isActive = isSaving || isHolding;
7
- const current = PsOptimisticReleaseService.next(remembered, rows, isActive, apply);
7
+ const current = PsOptimisticReleaseService.next(remembered, rows, isActive, isHolding, apply);
8
8
  if (current !== remembered)
9
9
  setRemembered(current);
10
10
  const { changes } = current;
@@ -21,6 +21,7 @@ export const usePsOptimisticRows = ({ apply, isHolding = false, isSaving, rows,
21
21
  changes: [...previous.changes, change],
22
22
  drainBase: null,
23
23
  wasActive: previous.wasActive,
24
+ wasHolding: previous.wasHolding,
24
25
  })), [rows]);
25
26
  const forgetLast = useCallback(() => setRemembered((previous) => ({ ...previous, changes: previous.changes.slice(0, -1) })), []);
26
27
  const rollback = useCallback(() => setRemembered(PsOptimisticReleaseService.empty(rows)), [rows]);
@@ -0,0 +1,31 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { PsRemoteReadService } from '../services/ps-remote-read-service.js';
3
+ export const usePsRemoteRead = ({ parse, reloadKey, url, }) => {
4
+ const [state, setState] = useState(() => PsRemoteReadService.initialState());
5
+ const parseRef = useRef(parse);
6
+ parseRef.current = parse;
7
+ useEffect(() => {
8
+ if (url === null)
9
+ return;
10
+ const controller = new AbortController();
11
+ PsRemoteReadService.read(url, parseRef.current, controller.signal).then((data) => {
12
+ if (controller.signal.aborted)
13
+ return;
14
+ setState({ data, error: null, settled: { reloadKey, url } });
15
+ }, (failure) => {
16
+ if (controller.signal.aborted)
17
+ return;
18
+ setState((current) => ({
19
+ data: current.data,
20
+ error: failure,
21
+ settled: { reloadKey, url },
22
+ }));
23
+ });
24
+ return () => controller.abort();
25
+ }, [reloadKey, url]);
26
+ return {
27
+ data: state.data,
28
+ error: state.error,
29
+ isLoading: PsRemoteReadService.isPending(state.settled, url, reloadKey),
30
+ };
31
+ };
@@ -0,0 +1,16 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { PsQueryParamConfig } from '../constants/ps-query-param-config.js';
3
+ import { PsQueryParamService } from '../services/ps-query-param-service.js';
4
+ import { usePsDebouncedValue } from './use-ps-debounced-value.js';
5
+ export const usePsTextParam = ([searchParams, setSearchParams], { debounceMs, name, resetParams }) => {
6
+ const raw = searchParams.get(name);
7
+ const committed = raw === null ? '' : raw;
8
+ const [value, setValue] = useState(committed);
9
+ const settled = usePsDebouncedValue(value, debounceMs);
10
+ useEffect(() => {
11
+ if (settled === committed)
12
+ return;
13
+ setSearchParams((current) => PsQueryParamService.withChoice(current, name, settled, '', resetParams), PsQueryParamConfig.setOptions);
14
+ }, [settled]);
15
+ return { committed, onChange: setValue, value };
16
+ };
package/dist/esm/index.js CHANGED
@@ -228,3 +228,33 @@ export * from './hooks/use-ps-choice-param.js';
228
228
  export * from './hooks/use-ps-flag-param.js';
229
229
  export * from './constants/ps-rerender-config.js';
230
230
  export * from './hooks/use-ps-rerender-at.js';
231
+ export * from './models/ps-remote-settled-data.js';
232
+ export * from './models/ps-remote-read-state.js';
233
+ export * from './interfaces/ps-remote-read-input.js';
234
+ export * from './interfaces/ps-remote-read-result.js';
235
+ export * from './services/ps-remote-read-service.js';
236
+ export * from './hooks/use-ps-remote-read.js';
237
+ export * from './interfaces/ps-cursor-pages-input.js';
238
+ export * from './interfaces/ps-cursor-pages-result.js';
239
+ export * from './models/ps-cursor-pages-state.js';
240
+ export * from './services/ps-cursor-pages-service.js';
241
+ export * from './hooks/use-ps-cursor-pages.js';
242
+ export * from './models/ps-text-param-options.js';
243
+ export * from './models/ps-text-param-data.js';
244
+ export * from './hooks/use-ps-text-param.js';
245
+ export * from './interfaces/ps-draft-state-options.js';
246
+ export * from './interfaces/ps-draft-state-result.js';
247
+ export * from './models/ps-draft-state-data.js';
248
+ export * from './services/ps-draft-state-service.js';
249
+ export * from './hooks/use-ps-draft-state.js';
250
+ export * from './constants/ps-empty-value-config.js';
251
+ export * from './components/ps-empty-value.js';
252
+ export * from './constants/ps-signed-value-classes.js';
253
+ export * from './services/ps-signed-value-service.js';
254
+ export * from './components/ps-signed-value.js';
255
+ export * from './constants/ps-image-status.js';
256
+ export * from './constants/ps-image-fit.js';
257
+ export * from './constants/ps-image-config.js';
258
+ export * from './models/ps-image-state-data.js';
259
+ export * from './services/ps-image-service.js';
260
+ export * from './components/ps-image.js';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ export class PsCursorPagesService {
2
+ static append(existing, page, keyOf) {
3
+ if (keyOf === undefined)
4
+ return [...existing, ...page];
5
+ const known = new Set(existing.map(keyOf));
6
+ return [...existing, ...page.filter((row) => !known.has(keyOf(row)))];
7
+ }
8
+ static hasMore(pageLength, pageSize) {
9
+ return pageLength >= pageSize;
10
+ }
11
+ static initialState() {
12
+ return { error: null, hasMore: false, isLoadingMore: false, rows: [], settled: null };
13
+ }
14
+ }
@@ -0,0 +1,11 @@
1
+ import { PsDeepEqualService } from './ps-deep-equal-service.js';
2
+ export class PsDraftStateService {
3
+ static fresh(initial) {
4
+ return { baseline: initial, value: structuredClone(initial) };
5
+ }
6
+ static isEqual(left, right, options) {
7
+ if (options !== undefined && options.isEqual !== undefined)
8
+ return options.isEqual(left, right);
9
+ return PsDeepEqualService.isEqual(left, right);
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { PsImageStatus } from '../constants/ps-image-status.js';
2
+ export class PsImageService {
3
+ static resolve(state, src) {
4
+ return state.src === src ? state.status : PsImageStatus.Loading;
5
+ }
6
+ static statusOf(element) {
7
+ if (!element.complete)
8
+ return PsImageStatus.Loading;
9
+ return element.naturalWidth > 0 ? PsImageStatus.Loaded : PsImageStatus.Error;
10
+ }
11
+ }