@pallasoft/ps-core-components 2.21.0 → 2.22.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.
@@ -1,10 +1,11 @@
1
1
  export interface PsNavigationProgressProps {
2
2
  className?: string;
3
3
  color?: string;
4
+ delayMs?: number;
4
5
  height?: number;
5
6
  isLoading?: boolean;
6
7
  label?: string;
7
8
  progress?: number;
8
9
  shadow?: boolean;
9
10
  }
10
- export declare const PsNavigationProgress: ({ className, color, height, isLoading, label, progress, shadow, }: PsNavigationProgressProps) => import("react").JSX.Element;
11
+ export declare const PsNavigationProgress: ({ className, color, delayMs, height, isLoading, label, progress, shadow, }: PsNavigationProgressProps) => import("react").JSX.Element;
@@ -5,12 +5,12 @@ const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
6
  const ps_navigation_progress_config_1 = require("../constants/ps-navigation-progress-config");
7
7
  const use_ps_navigation_progress_1 = require("../hooks/use-ps-navigation-progress");
8
- const PsNavigationProgress = ({ className, color = ps_navigation_progress_config_1.PsNavigationProgressConfig.defaultColor, height = ps_navigation_progress_config_1.PsNavigationProgressConfig.defaultHeight, isLoading, label = ps_navigation_progress_config_1.PsNavigationProgressConfig.defaultLabel, progress, shadow = true, }) => {
8
+ const PsNavigationProgress = ({ className, color = ps_navigation_progress_config_1.PsNavigationProgressConfig.defaultColor, delayMs = ps_navigation_progress_config_1.PsNavigationProgressConfig.showDelayMs, height = ps_navigation_progress_config_1.PsNavigationProgressConfig.defaultHeight, isLoading, label = ps_navigation_progress_config_1.PsNavigationProgressConfig.defaultLabel, progress, shadow = true, }) => {
9
9
  const { isAnimated, isVisible, width } = (0, use_ps_navigation_progress_1.usePsNavigationProgress)(isLoading !== undefined
10
10
  ? isLoading
11
11
  : progress !== undefined &&
12
12
  progress > 0 &&
13
- progress < ps_navigation_progress_config_1.PsNavigationProgressConfig.completeWidth);
13
+ progress < ps_navigation_progress_config_1.PsNavigationProgressConfig.completeWidth, delayMs);
14
14
  return ((0, jsx_runtime_1.jsx)("div", { "aria-hidden": isVisible ? undefined : 'true', "aria-label": label, "aria-valuemax": 100, "aria-valuemin": 0, "aria-valuenow": Math.round(width), className: ps_design_system_1.PallasoftClassNameService.merge('pointer-events-none fixed inset-x-0 top-0', className), role: 'progressbar', style: { height, zIndex: ps_design_system_1.PallasoftZIndex.Toast }, children: (0, jsx_runtime_1.jsx)("div", { className: 'h-full origin-left ease-out', style: {
15
15
  backgroundColor: color,
16
16
  boxShadow: shadow ? `0 0 10px ${color}, 0 0 5px ${color}` : undefined,
@@ -4,6 +4,7 @@ export declare const PsNavigationProgressConfig: {
4
4
  readonly defaultHeight: 3;
5
5
  readonly defaultLabel: "Page loading";
6
6
  readonly fadeMs: 320;
7
+ readonly showDelayMs: 150;
7
8
  readonly startWidth: 12;
8
9
  readonly transitionMs: 240;
9
10
  readonly trickleFactor: 0.14;
@@ -7,6 +7,7 @@ exports.PsNavigationProgressConfig = {
7
7
  defaultHeight: 3,
8
8
  defaultLabel: 'Page loading',
9
9
  fadeMs: 320,
10
+ showDelayMs: 150,
10
11
  startWidth: 12,
11
12
  transitionMs: 240,
12
13
  trickleFactor: 0.14,
@@ -2,12 +2,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { PallasoftClassNameService, PallasoftZIndex } from '@pallasoft/ps-design-system';
3
3
  import { PsNavigationProgressConfig } from '../constants/ps-navigation-progress-config';
4
4
  import { usePsNavigationProgress } from '../hooks/use-ps-navigation-progress';
5
- export const PsNavigationProgress = ({ className, color = PsNavigationProgressConfig.defaultColor, height = PsNavigationProgressConfig.defaultHeight, isLoading, label = PsNavigationProgressConfig.defaultLabel, progress, shadow = true, }) => {
5
+ export const PsNavigationProgress = ({ className, color = PsNavigationProgressConfig.defaultColor, delayMs = PsNavigationProgressConfig.showDelayMs, height = PsNavigationProgressConfig.defaultHeight, isLoading, label = PsNavigationProgressConfig.defaultLabel, progress, shadow = true, }) => {
6
6
  const { isAnimated, isVisible, width } = usePsNavigationProgress(isLoading !== undefined
7
7
  ? isLoading
8
8
  : progress !== undefined &&
9
9
  progress > 0 &&
10
- progress < PsNavigationProgressConfig.completeWidth);
10
+ progress < PsNavigationProgressConfig.completeWidth, delayMs);
11
11
  return (_jsx("div", { "aria-hidden": isVisible ? undefined : 'true', "aria-label": label, "aria-valuemax": 100, "aria-valuemin": 0, "aria-valuenow": Math.round(width), className: PallasoftClassNameService.merge('pointer-events-none fixed inset-x-0 top-0', className), role: 'progressbar', style: { height, zIndex: PallasoftZIndex.Toast }, children: _jsx("div", { className: 'h-full origin-left ease-out', style: {
12
12
  backgroundColor: color,
13
13
  boxShadow: shadow ? `0 0 10px ${color}, 0 0 5px ${color}` : undefined,
@@ -4,6 +4,7 @@ export const PsNavigationProgressConfig = {
4
4
  defaultHeight: 3,
5
5
  defaultLabel: 'Page loading',
6
6
  fadeMs: 320,
7
+ showDelayMs: 150,
7
8
  startWidth: 12,
8
9
  transitionMs: 240,
9
10
  trickleFactor: 0.14,
@@ -0,0 +1,31 @@
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 latest.current.send(batch).catch(() => {
18
+ const dropped = pending.current;
19
+ pending.current = [];
20
+ latest.current.onFailed(batch, dropped);
21
+ });
22
+ }
23
+ isRunning.current = false;
24
+ latest.current.onDrained();
25
+ }, []);
26
+ const enqueue = useCallback((change) => {
27
+ pending.current = [...pending.current, change];
28
+ void run();
29
+ }, [run]);
30
+ return { enqueue };
31
+ };
@@ -1,7 +1,7 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
2
  import { PsNavigationProgressConfig } from '../constants/ps-navigation-progress-config';
3
3
  const hidden = { isAnimated: false, isVisible: false, width: 0 };
4
- export const usePsNavigationProgress = (isLoading) => {
4
+ export const usePsNavigationProgress = (isLoading, delayMs = PsNavigationProgressConfig.showDelayMs) => {
5
5
  const [state, setState] = useState(hidden);
6
6
  const timers = useRef([]);
7
7
  useEffect(() => {
@@ -14,17 +14,23 @@ export const usePsNavigationProgress = (isLoading) => {
14
14
  };
15
15
  clear();
16
16
  if (isLoading) {
17
- setState({ isAnimated: false, isVisible: true, width: 0 });
18
- timers.current.push(window.setTimeout(() => setState({
19
- isAnimated: true,
20
- isVisible: true,
21
- width: PsNavigationProgressConfig.startWidth,
22
- }), 0), window.setInterval(() => setState((current) => ({
23
- ...current,
24
- width: current.width +
25
- (PsNavigationProgressConfig.trickleMax - current.width) *
26
- PsNavigationProgressConfig.trickleFactor,
27
- })), PsNavigationProgressConfig.trickleMs));
17
+ const start = () => {
18
+ setState({ isAnimated: false, isVisible: true, width: 0 });
19
+ timers.current.push(window.setTimeout(() => setState({
20
+ isAnimated: true,
21
+ isVisible: true,
22
+ width: PsNavigationProgressConfig.startWidth,
23
+ }), 0), window.setInterval(() => setState((current) => ({
24
+ ...current,
25
+ width: current.width +
26
+ (PsNavigationProgressConfig.trickleMax - current.width) *
27
+ PsNavigationProgressConfig.trickleFactor,
28
+ })), PsNavigationProgressConfig.trickleMs));
29
+ };
30
+ if (delayMs > 0)
31
+ timers.current.push(window.setTimeout(start, delayMs));
32
+ else
33
+ start();
28
34
  return clear;
29
35
  }
30
36
  setState((current) => current.isVisible
@@ -36,6 +42,6 @@ export const usePsNavigationProgress = (isLoading) => {
36
42
  : current);
37
43
  timers.current.push(window.setTimeout(() => setState((current) => ({ ...current, isVisible: false })), PsNavigationProgressConfig.transitionMs), window.setTimeout(() => setState(hidden), PsNavigationProgressConfig.transitionMs + PsNavigationProgressConfig.fadeMs));
38
44
  return clear;
39
- }, [isLoading]);
45
+ }, [delayMs, isLoading]);
40
46
  return state;
41
47
  };
package/dist/esm/index.js CHANGED
@@ -56,6 +56,9 @@ export * from './models/ps-element-size';
56
56
  export * from './models/ps-voice-orb';
57
57
  export * from './models/ps-navigation-progress';
58
58
  export * from './hooks/use-ps-debounced-value';
59
+ export * from './hooks/use-ps-change-queue';
60
+ export * from './interfaces/ps-change-queue-input';
61
+ export * from './interfaces/ps-change-queue-result';
59
62
  export * from './hooks/use-ps-optimistic-rows';
60
63
  export * from './interfaces/ps-optimistic-rows-input';
61
64
  export * from './interfaces/ps-optimistic-rows-result';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
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>;
@@ -0,0 +1,35 @@
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 latest.current.send(batch).catch(() => {
21
+ const dropped = pending.current;
22
+ pending.current = [];
23
+ latest.current.onFailed(batch, dropped);
24
+ });
25
+ }
26
+ isRunning.current = false;
27
+ latest.current.onDrained();
28
+ }, []);
29
+ const enqueue = (0, react_1.useCallback)((change) => {
30
+ pending.current = [...pending.current, change];
31
+ void run();
32
+ }, [run]);
33
+ return { enqueue };
34
+ };
35
+ exports.usePsChangeQueue = usePsChangeQueue;
@@ -1,2 +1,2 @@
1
1
  import { PsNavigationProgressState } from '../models/ps-navigation-progress';
2
- export declare const usePsNavigationProgress: (isLoading: boolean) => PsNavigationProgressState;
2
+ export declare const usePsNavigationProgress: (isLoading: boolean, delayMs?: number) => PsNavigationProgressState;
@@ -4,7 +4,7 @@ exports.usePsNavigationProgress = void 0;
4
4
  const react_1 = require("react");
5
5
  const ps_navigation_progress_config_1 = require("../constants/ps-navigation-progress-config");
6
6
  const hidden = { isAnimated: false, isVisible: false, width: 0 };
7
- const usePsNavigationProgress = (isLoading) => {
7
+ const usePsNavigationProgress = (isLoading, delayMs = ps_navigation_progress_config_1.PsNavigationProgressConfig.showDelayMs) => {
8
8
  const [state, setState] = (0, react_1.useState)(hidden);
9
9
  const timers = (0, react_1.useRef)([]);
10
10
  (0, react_1.useEffect)(() => {
@@ -17,17 +17,23 @@ const usePsNavigationProgress = (isLoading) => {
17
17
  };
18
18
  clear();
19
19
  if (isLoading) {
20
- setState({ isAnimated: false, isVisible: true, width: 0 });
21
- timers.current.push(window.setTimeout(() => setState({
22
- isAnimated: true,
23
- isVisible: true,
24
- width: ps_navigation_progress_config_1.PsNavigationProgressConfig.startWidth,
25
- }), 0), window.setInterval(() => setState((current) => ({
26
- ...current,
27
- width: current.width +
28
- (ps_navigation_progress_config_1.PsNavigationProgressConfig.trickleMax - current.width) *
29
- ps_navigation_progress_config_1.PsNavigationProgressConfig.trickleFactor,
30
- })), ps_navigation_progress_config_1.PsNavigationProgressConfig.trickleMs));
20
+ const start = () => {
21
+ setState({ isAnimated: false, isVisible: true, width: 0 });
22
+ timers.current.push(window.setTimeout(() => setState({
23
+ isAnimated: true,
24
+ isVisible: true,
25
+ width: ps_navigation_progress_config_1.PsNavigationProgressConfig.startWidth,
26
+ }), 0), window.setInterval(() => setState((current) => ({
27
+ ...current,
28
+ width: current.width +
29
+ (ps_navigation_progress_config_1.PsNavigationProgressConfig.trickleMax - current.width) *
30
+ ps_navigation_progress_config_1.PsNavigationProgressConfig.trickleFactor,
31
+ })), ps_navigation_progress_config_1.PsNavigationProgressConfig.trickleMs));
32
+ };
33
+ if (delayMs > 0)
34
+ timers.current.push(window.setTimeout(start, delayMs));
35
+ else
36
+ start();
31
37
  return clear;
32
38
  }
33
39
  setState((current) => current.isVisible
@@ -39,7 +45,7 @@ const usePsNavigationProgress = (isLoading) => {
39
45
  : current);
40
46
  timers.current.push(window.setTimeout(() => setState((current) => ({ ...current, isVisible: false })), ps_navigation_progress_config_1.PsNavigationProgressConfig.transitionMs), window.setTimeout(() => setState(hidden), ps_navigation_progress_config_1.PsNavigationProgressConfig.transitionMs + ps_navigation_progress_config_1.PsNavigationProgressConfig.fadeMs));
41
47
  return clear;
42
- }, [isLoading]);
48
+ }, [delayMs, isLoading]);
43
49
  return state;
44
50
  };
45
51
  exports.usePsNavigationProgress = usePsNavigationProgress;
package/dist/index.d.ts CHANGED
@@ -56,6 +56,9 @@ export * from './models/ps-element-size';
56
56
  export * from './models/ps-voice-orb';
57
57
  export * from './models/ps-navigation-progress';
58
58
  export * from './hooks/use-ps-debounced-value';
59
+ export * from './hooks/use-ps-change-queue';
60
+ export * from './interfaces/ps-change-queue-input';
61
+ export * from './interfaces/ps-change-queue-result';
59
62
  export * from './hooks/use-ps-optimistic-rows';
60
63
  export * from './interfaces/ps-optimistic-rows-input';
61
64
  export * from './interfaces/ps-optimistic-rows-result';
package/dist/index.js CHANGED
@@ -72,6 +72,9 @@ __exportStar(require("./models/ps-element-size"), exports);
72
72
  __exportStar(require("./models/ps-voice-orb"), exports);
73
73
  __exportStar(require("./models/ps-navigation-progress"), exports);
74
74
  __exportStar(require("./hooks/use-ps-debounced-value"), exports);
75
+ __exportStar(require("./hooks/use-ps-change-queue"), exports);
76
+ __exportStar(require("./interfaces/ps-change-queue-input"), exports);
77
+ __exportStar(require("./interfaces/ps-change-queue-result"), exports);
75
78
  __exportStar(require("./hooks/use-ps-optimistic-rows"), exports);
76
79
  __exportStar(require("./interfaces/ps-optimistic-rows-input"), exports);
77
80
  __exportStar(require("./interfaces/ps-optimistic-rows-result"), exports);
@@ -0,0 +1,5 @@
1
+ export interface PsChangeQueueInput<Change> {
2
+ onDrained: () => void;
3
+ onFailed: (failed: Change[], dropped: Change[]) => void;
4
+ send: (changes: Change[]) => Promise<unknown>;
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ export interface PsChangeQueueResult<Change> {
2
+ enqueue: (change: Change) => void;
3
+ }
@@ -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.21.0",
3
+ "version": "2.22.1",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/esm/index.js",