@pallasoft/ps-core-components 2.27.0 → 2.28.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.
@@ -0,0 +1,12 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface PsOverviewCardProps {
3
+ action?: ReactNode;
4
+ badge?: ReactNode;
5
+ children?: ReactNode;
6
+ className?: string;
7
+ footer?: ReactNode;
8
+ icon?: ReactNode;
9
+ summary?: string;
10
+ title: string;
11
+ }
12
+ export declare const PsOverviewCard: ({ action, badge, children, className, footer, icon, summary, title, }: PsOverviewCardProps) => import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsOverviewCard = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const PsOverviewCard = ({ action, badge, children, className, footer, icon, summary, title, }) => ((0, jsx_runtime_1.jsxs)("section", { className: ps_design_system_1.PallasoftClassNameService.merge('flex min-w-0 flex-col gap-4 rounded-ps-panel border border-line bg-surface p-5', className), children: [(0, jsx_runtime_1.jsxs)("header", { className: 'flex min-w-0 items-start gap-3', children: [icon ? (0, jsx_runtime_1.jsx)("span", { className: 'ps-icon-surface h-10 w-10 shrink-0', children: icon }) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'flex min-w-0 flex-1 flex-col gap-1', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-wrap items-center gap-2', children: [(0, jsx_runtime_1.jsx)("h2", { className: 'text-base font-semibold text-ink', children: title }), badge] }), summary ? (0, jsx_runtime_1.jsx)("p", { className: 'break-words text-sm leading-6 text-ink/60', children: summary }) : null] }), action ? (0, jsx_runtime_1.jsx)("div", { className: 'flex shrink-0 items-center gap-2', children: action }) : null] }), children ? (0, jsx_runtime_1.jsx)("div", { className: 'flex min-w-0 flex-col gap-1', children: children }) : null, footer ? (0, jsx_runtime_1.jsx)("div", { className: 'flex flex-wrap items-center gap-2', children: footer }) : null] }));
7
+ exports.PsOverviewCard = PsOverviewCard;
@@ -0,0 +1,3 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
3
+ export const PsOverviewCard = ({ action, badge, children, className, footer, icon, summary, title, }) => (_jsxs("section", { className: PallasoftClassNameService.merge('flex min-w-0 flex-col gap-4 rounded-ps-panel border border-line bg-surface p-5', className), children: [_jsxs("header", { className: 'flex min-w-0 items-start gap-3', children: [icon ? _jsx("span", { className: 'ps-icon-surface h-10 w-10 shrink-0', children: icon }) : null, _jsxs("div", { className: 'flex min-w-0 flex-1 flex-col gap-1', children: [_jsxs("div", { className: 'flex flex-wrap items-center gap-2', children: [_jsx("h2", { className: 'text-base font-semibold text-ink', children: title }), badge] }), summary ? _jsx("p", { className: 'break-words text-sm leading-6 text-ink/60', children: summary }) : null] }), action ? _jsx("div", { className: 'flex shrink-0 items-center gap-2', children: action }) : null] }), children ? _jsx("div", { className: 'flex min-w-0 flex-col gap-1', children: children }) : null, footer ? _jsx("div", { className: 'flex flex-wrap items-center gap-2', children: footer }) : null] }));
@@ -0,0 +1,68 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { usePsChangeQueue } from './use-ps-change-queue';
3
+ import { usePsOptimisticRows } from './use-ps-optimistic-rows';
4
+ export const usePsOptimisticFetcher = ({ apply, fetcher, isHolding = false, isSuccess, merge, revalidator, rows, }) => {
5
+ const [isUnsettled, setIsUnsettled] = useState(false);
6
+ const submitted = useRef(0);
7
+ const settle = useRef(null);
8
+ const sawBusy = useRef(false);
9
+ const dataBefore = useRef(fetcher.data);
10
+ const dataSeen = useRef(fetcher.data);
11
+ const { forgetLast, remember, rows: shown, } = usePsOptimisticRows({
12
+ apply,
13
+ isHolding,
14
+ isSaving: isUnsettled || fetcher.state !== 'idle' || revalidator.state !== 'idle',
15
+ rows,
16
+ });
17
+ useEffect(() => {
18
+ dataSeen.current = fetcher.data;
19
+ if (settle.current === null)
20
+ return;
21
+ if (fetcher.state !== 'idle') {
22
+ sawBusy.current = true;
23
+ return;
24
+ }
25
+ if (!sawBusy.current && fetcher.data === dataBefore.current)
26
+ return;
27
+ const done = settle.current;
28
+ settle.current = null;
29
+ sawBusy.current = false;
30
+ done(fetcher.data);
31
+ }, [fetcher.data, fetcher.state]);
32
+ const submitOne = useCallback((values) => new Promise((resolve) => {
33
+ settle.current = resolve;
34
+ sawBusy.current = false;
35
+ dataBefore.current = dataSeen.current;
36
+ void fetcher.submit(values, { method: 'post' });
37
+ }), [fetcher]);
38
+ const send = useCallback(async (queued) => {
39
+ for (const submission of merge === undefined ? queued : merge(queued)) {
40
+ const result = await submitOne(submission);
41
+ if (!isSuccess(result))
42
+ throw result;
43
+ }
44
+ }, [isSuccess, merge, submitOne]);
45
+ const revalidate = useCallback(() => {
46
+ const through = submitted.current;
47
+ void Promise.resolve(revalidator.revalidate()).finally(() => {
48
+ if (submitted.current === through)
49
+ setIsUnsettled(false);
50
+ });
51
+ }, [revalidator]);
52
+ const forgetFailed = useCallback((failed, dropped) => {
53
+ for (let index = 0; index < failed.length + dropped.length; index += 1)
54
+ forgetLast();
55
+ }, [forgetLast]);
56
+ const queue = usePsChangeQueue({
57
+ onDrained: revalidate,
58
+ onFailed: forgetFailed,
59
+ send,
60
+ });
61
+ const submit = useCallback((values) => {
62
+ submitted.current += 1;
63
+ setIsUnsettled(true);
64
+ remember(values);
65
+ queue.enqueue(values);
66
+ }, [queue, remember]);
67
+ return { rows: shown, submit };
68
+ };
package/dist/esm/index.js CHANGED
@@ -75,6 +75,12 @@ export * from './hooks/use-ps-change-queue';
75
75
  export * from './interfaces/ps-change-queue-input';
76
76
  export * from './interfaces/ps-change-queue-result';
77
77
  export * from './hooks/use-ps-optimistic-rows';
78
+ export * from './hooks/use-ps-optimistic-fetcher';
79
+ export * from './interfaces/ps-fetcher-like';
80
+ export * from './interfaces/ps-fetcher-submit-options';
81
+ export * from './interfaces/ps-optimistic-fetcher-input';
82
+ export * from './interfaces/ps-optimistic-fetcher-result';
83
+ export * from './interfaces/ps-revalidator-like';
78
84
  export * from './interfaces/ps-optimistic-rows-input';
79
85
  export * from './interfaces/ps-optimistic-rows-result';
80
86
  export * from './hooks/use-ps-navigation-progress';
@@ -129,6 +135,7 @@ export * from './models/ps-theme-mode-state';
129
135
  export * from './models/ps-tool-card-data';
130
136
  export * from './components/ps-capped-list';
131
137
  export * from './components/ps-detail-list';
138
+ export * from './components/ps-overview-card';
132
139
  export * from './components/ps-form-footer';
133
140
  export * from './components/ps-start-highlight';
134
141
  export * from './components/ps-start-panel';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { PsOptimisticFetcherInput } from '../interfaces/ps-optimistic-fetcher-input';
2
+ import type { PsOptimisticFetcherResult } from '../interfaces/ps-optimistic-fetcher-result';
3
+ export declare const usePsOptimisticFetcher: <Row, Data>({ apply, fetcher, isHolding, isSuccess, merge, revalidator, rows, }: PsOptimisticFetcherInput<Row, Data>) => PsOptimisticFetcherResult<Row>;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePsOptimisticFetcher = void 0;
4
+ const react_1 = require("react");
5
+ const use_ps_change_queue_1 = require("./use-ps-change-queue");
6
+ const use_ps_optimistic_rows_1 = require("./use-ps-optimistic-rows");
7
+ const usePsOptimisticFetcher = ({ apply, fetcher, isHolding = false, isSuccess, merge, revalidator, rows, }) => {
8
+ const [isUnsettled, setIsUnsettled] = (0, react_1.useState)(false);
9
+ const submitted = (0, react_1.useRef)(0);
10
+ const settle = (0, react_1.useRef)(null);
11
+ const sawBusy = (0, react_1.useRef)(false);
12
+ const dataBefore = (0, react_1.useRef)(fetcher.data);
13
+ const dataSeen = (0, react_1.useRef)(fetcher.data);
14
+ const { forgetLast, remember, rows: shown, } = (0, use_ps_optimistic_rows_1.usePsOptimisticRows)({
15
+ apply,
16
+ isHolding,
17
+ isSaving: isUnsettled || fetcher.state !== 'idle' || revalidator.state !== 'idle',
18
+ rows,
19
+ });
20
+ (0, react_1.useEffect)(() => {
21
+ dataSeen.current = fetcher.data;
22
+ if (settle.current === null)
23
+ return;
24
+ if (fetcher.state !== 'idle') {
25
+ sawBusy.current = true;
26
+ return;
27
+ }
28
+ if (!sawBusy.current && fetcher.data === dataBefore.current)
29
+ return;
30
+ const done = settle.current;
31
+ settle.current = null;
32
+ sawBusy.current = false;
33
+ done(fetcher.data);
34
+ }, [fetcher.data, fetcher.state]);
35
+ const submitOne = (0, react_1.useCallback)((values) => new Promise((resolve) => {
36
+ settle.current = resolve;
37
+ sawBusy.current = false;
38
+ dataBefore.current = dataSeen.current;
39
+ void fetcher.submit(values, { method: 'post' });
40
+ }), [fetcher]);
41
+ const send = (0, react_1.useCallback)(async (queued) => {
42
+ for (const submission of merge === undefined ? queued : merge(queued)) {
43
+ const result = await submitOne(submission);
44
+ if (!isSuccess(result))
45
+ throw result;
46
+ }
47
+ }, [isSuccess, merge, submitOne]);
48
+ const revalidate = (0, react_1.useCallback)(() => {
49
+ const through = submitted.current;
50
+ void Promise.resolve(revalidator.revalidate()).finally(() => {
51
+ if (submitted.current === through)
52
+ setIsUnsettled(false);
53
+ });
54
+ }, [revalidator]);
55
+ const forgetFailed = (0, react_1.useCallback)((failed, dropped) => {
56
+ for (let index = 0; index < failed.length + dropped.length; index += 1)
57
+ forgetLast();
58
+ }, [forgetLast]);
59
+ const queue = (0, use_ps_change_queue_1.usePsChangeQueue)({
60
+ onDrained: revalidate,
61
+ onFailed: forgetFailed,
62
+ send,
63
+ });
64
+ const submit = (0, react_1.useCallback)((values) => {
65
+ submitted.current += 1;
66
+ setIsUnsettled(true);
67
+ remember(values);
68
+ queue.enqueue(values);
69
+ }, [queue, remember]);
70
+ return { rows: shown, submit };
71
+ };
72
+ exports.usePsOptimisticFetcher = usePsOptimisticFetcher;
package/dist/index.d.ts CHANGED
@@ -75,6 +75,12 @@ export * from './hooks/use-ps-change-queue';
75
75
  export * from './interfaces/ps-change-queue-input';
76
76
  export * from './interfaces/ps-change-queue-result';
77
77
  export * from './hooks/use-ps-optimistic-rows';
78
+ export * from './hooks/use-ps-optimistic-fetcher';
79
+ export * from './interfaces/ps-fetcher-like';
80
+ export * from './interfaces/ps-fetcher-submit-options';
81
+ export * from './interfaces/ps-optimistic-fetcher-input';
82
+ export * from './interfaces/ps-optimistic-fetcher-result';
83
+ export * from './interfaces/ps-revalidator-like';
78
84
  export * from './interfaces/ps-optimistic-rows-input';
79
85
  export * from './interfaces/ps-optimistic-rows-result';
80
86
  export * from './hooks/use-ps-navigation-progress';
@@ -129,6 +135,7 @@ export * from './models/ps-theme-mode-state';
129
135
  export * from './models/ps-tool-card-data';
130
136
  export * from './components/ps-capped-list';
131
137
  export * from './components/ps-detail-list';
138
+ export * from './components/ps-overview-card';
132
139
  export * from './components/ps-form-footer';
133
140
  export * from './components/ps-start-highlight';
134
141
  export * from './components/ps-start-panel';
package/dist/index.js CHANGED
@@ -91,6 +91,12 @@ __exportStar(require("./hooks/use-ps-change-queue"), exports);
91
91
  __exportStar(require("./interfaces/ps-change-queue-input"), exports);
92
92
  __exportStar(require("./interfaces/ps-change-queue-result"), exports);
93
93
  __exportStar(require("./hooks/use-ps-optimistic-rows"), exports);
94
+ __exportStar(require("./hooks/use-ps-optimistic-fetcher"), exports);
95
+ __exportStar(require("./interfaces/ps-fetcher-like"), exports);
96
+ __exportStar(require("./interfaces/ps-fetcher-submit-options"), exports);
97
+ __exportStar(require("./interfaces/ps-optimistic-fetcher-input"), exports);
98
+ __exportStar(require("./interfaces/ps-optimistic-fetcher-result"), exports);
99
+ __exportStar(require("./interfaces/ps-revalidator-like"), exports);
94
100
  __exportStar(require("./interfaces/ps-optimistic-rows-input"), exports);
95
101
  __exportStar(require("./interfaces/ps-optimistic-rows-result"), exports);
96
102
  __exportStar(require("./hooks/use-ps-navigation-progress"), exports);
@@ -145,6 +151,7 @@ __exportStar(require("./models/ps-theme-mode-state"), exports);
145
151
  __exportStar(require("./models/ps-tool-card-data"), exports);
146
152
  __exportStar(require("./components/ps-capped-list"), exports);
147
153
  __exportStar(require("./components/ps-detail-list"), exports);
154
+ __exportStar(require("./components/ps-overview-card"), exports);
148
155
  __exportStar(require("./components/ps-form-footer"), exports);
149
156
  __exportStar(require("./components/ps-start-highlight"), exports);
150
157
  __exportStar(require("./components/ps-start-panel"), exports);
@@ -0,0 +1,6 @@
1
+ import type { PsFetcherSubmitOptions } from './ps-fetcher-submit-options';
2
+ export interface PsFetcherLike<Data> {
3
+ data: Data | undefined;
4
+ state: string;
5
+ submit: (values: Record<string, string>, options: PsFetcherSubmitOptions) => unknown;
6
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ export interface PsFetcherSubmitOptions {
2
+ method: 'post';
3
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,11 @@
1
+ import type { PsFetcherLike } from './ps-fetcher-like';
2
+ import type { PsRevalidatorLike } from './ps-revalidator-like';
3
+ export interface PsOptimisticFetcherInput<Row, Data> {
4
+ apply: (rows: Row[], changes: Record<string, string>[]) => Row[];
5
+ fetcher: PsFetcherLike<Data>;
6
+ isHolding?: boolean;
7
+ isSuccess: (data: Data | undefined) => boolean;
8
+ merge?: (changes: Record<string, string>[]) => Record<string, string>[];
9
+ revalidator: PsRevalidatorLike;
10
+ rows: Row[];
11
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ export interface PsOptimisticFetcherResult<Row> {
2
+ rows: Row[];
3
+ submit: (values: Record<string, string>) => void;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ export interface PsRevalidatorLike {
2
+ revalidate: () => Promise<void> | void;
3
+ state: string;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pallasoft/ps-core-components",
3
- "version": "2.27.0",
3
+ "version": "2.28.0",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/esm/index.js",