@pallasoft/ps-core-components 2.27.0 → 2.29.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 (38) hide show
  1. package/dist/components/ps-overview-card.d.ts +12 -0
  2. package/dist/components/ps-overview-card.js +7 -0
  3. package/dist/components/ps-rating-display.d.ts +9 -0
  4. package/dist/components/ps-rating-display.js +12 -0
  5. package/dist/components/ps-sortable-list.d.ts +1 -1
  6. package/dist/components/ps-sortable-list.js +2 -2
  7. package/dist/constants/ps-rating-display-config.d.ts +10 -0
  8. package/dist/constants/ps-rating-display-config.js +14 -0
  9. package/dist/constants/ps-rating-display-size.d.ts +5 -0
  10. package/dist/constants/ps-rating-display-size.js +9 -0
  11. package/dist/esm/components/ps-overview-card.js +3 -0
  12. package/dist/esm/components/ps-rating-display.js +8 -0
  13. package/dist/esm/components/ps-sortable-list.js +3 -3
  14. package/dist/esm/constants/ps-rating-display-config.js +11 -0
  15. package/dist/esm/constants/ps-rating-display-size.js +6 -0
  16. package/dist/esm/hooks/use-ps-optimistic-fetcher.js +68 -0
  17. package/dist/esm/index.js +9 -0
  18. package/dist/esm/interfaces/ps-fetcher-like.js +1 -0
  19. package/dist/esm/interfaces/ps-fetcher-submit-options.js +1 -0
  20. package/dist/esm/interfaces/ps-optimistic-fetcher-input.js +1 -0
  21. package/dist/esm/interfaces/ps-optimistic-fetcher-result.js +1 -0
  22. package/dist/esm/interfaces/ps-revalidator-like.js +1 -0
  23. package/dist/hooks/use-ps-optimistic-fetcher.d.ts +3 -0
  24. package/dist/hooks/use-ps-optimistic-fetcher.js +72 -0
  25. package/dist/index.d.ts +9 -0
  26. package/dist/index.js +9 -0
  27. package/dist/interfaces/ps-fetcher-like.d.ts +6 -0
  28. package/dist/interfaces/ps-fetcher-like.js +2 -0
  29. package/dist/interfaces/ps-fetcher-submit-options.d.ts +3 -0
  30. package/dist/interfaces/ps-fetcher-submit-options.js +2 -0
  31. package/dist/interfaces/ps-optimistic-fetcher-input.d.ts +11 -0
  32. package/dist/interfaces/ps-optimistic-fetcher-input.js +2 -0
  33. package/dist/interfaces/ps-optimistic-fetcher-result.d.ts +4 -0
  34. package/dist/interfaces/ps-optimistic-fetcher-result.js +2 -0
  35. package/dist/interfaces/ps-revalidator-like.d.ts +4 -0
  36. package/dist/interfaces/ps-revalidator-like.js +2 -0
  37. package/dist/models/ps-sortable-list.d.ts +1 -0
  38. package/package.json +1 -1
@@ -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,9 @@
1
+ import { PsRatingDisplaySize } from '../constants/ps-rating-display-size';
2
+ export interface PsRatingDisplayProps {
3
+ className?: string;
4
+ label: string;
5
+ max?: number;
6
+ score: number;
7
+ size?: PsRatingDisplaySize;
8
+ }
9
+ export declare const PsRatingDisplay: ({ className, label, max, score, size, }: PsRatingDisplayProps) => import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsRatingDisplay = 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_icons_1 = require("@pallasoft/ps-icons");
7
+ const ps_rating_display_config_1 = require("../constants/ps-rating-display-config");
8
+ const ps_rating_display_size_1 = require("../constants/ps-rating-display-size");
9
+ const PsRatingDisplay = ({ className, label, max = ps_rating_display_config_1.PsRatingDisplayConfig.defaultMax, score, size = ps_rating_display_size_1.PsRatingDisplaySize.Medium, }) => ((0, jsx_runtime_1.jsx)("div", { "aria-label": label, className: ps_design_system_1.PallasoftClassNameService.merge('flex items-center gap-1', className), role: 'img', children: Array.from({ length: max }, (_, index) => index + 1).map((value) => ((0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { className: ps_design_system_1.PallasoftClassNameService.merge(ps_rating_display_config_1.PsRatingDisplayConfig.iconClassNames[size], value <= score
10
+ ? ps_rating_display_config_1.PsRatingDisplayConfig.filledClassName
11
+ : ps_rating_display_config_1.PsRatingDisplayConfig.emptyClassName), iconKey: ps_icons_1.PsIconKey.Star }, value))) }));
12
+ exports.PsRatingDisplay = PsRatingDisplay;
@@ -19,4 +19,4 @@ export interface PsSortableListRowProps<TItem extends PsSortableListItem> {
19
19
  item: TItem;
20
20
  renderItem: (options: PsSortableListRenderOptions<TItem>) => JSX.Element | null;
21
21
  }
22
- export declare const PsSortableList: <TItem extends PsSortableListItem>({ announcements: texts, className, getHandleLabel, getItemClassName, handleClassName, handleIcon, instructions, role, items, onMove, renderItem, }: PsSortableListProps<TItem>) => JSX.Element;
22
+ export declare const PsSortableList: <TItem extends PsSortableListItem>({ announcements: texts, className, getHandleLabel, getItemClassName, handleClassName, handleIcon, instructions, isHorizontal, role, items, onMove, renderItem, }: PsSortableListProps<TItem>) => JSX.Element;
@@ -30,7 +30,7 @@ const PsSortableListRow = ({ className, handleClassName, handleIcon, getHandleLa
30
30
  transition: isReduced ? undefined : transition,
31
31
  }, children: rendered }));
32
32
  };
33
- const PsSortableList = ({ announcements: texts, className, getHandleLabel, getItemClassName, handleClassName, handleIcon, instructions, role, items, onMove, renderItem, }) => {
33
+ const PsSortableList = ({ announcements: texts, className, getHandleLabel, getItemClassName, handleClassName, handleIcon, instructions, isHorizontal = false, role, items, onMove, renderItem, }) => {
34
34
  const [activeId, setActiveId] = (0, react_1.useState)(null);
35
35
  const sensors = (0, use_ps_drag_sensors_1.usePsDragSensors)();
36
36
  const activeIndex = items.findIndex((item) => item.id === activeId);
@@ -66,7 +66,7 @@ const PsSortableList = ({ announcements: texts, className, getHandleLabel, getIt
66
66
  onMove(String(active.id), from, to);
67
67
  };
68
68
  const activeGroup = activeItem === null ? undefined : activeItem.groupKey;
69
- return ((0, jsx_runtime_1.jsxs)(core_1.DndContext, { accessibility: { announcements, screenReaderInstructions: { draggable: instructions } }, collisionDetection: core_1.closestCenter, onDragCancel: () => setActiveId(null), onDragEnd: handleDragEnd, onDragStart: ({ active }) => setActiveId(String(active.id)), sensors: sensors, children: [(0, jsx_runtime_1.jsx)(sortable_1.SortableContext, { items: items.map((item) => item.id), strategy: sortable_1.verticalListSortingStrategy, children: (0, jsx_runtime_1.jsx)("div", { className: className, role: role, children: items.map((item, index) => ((0, jsx_runtime_1.jsx)(PsSortableListRow, { className: itemClassName(item), getHandleLabel: getHandleLabel, handleClassName: handleClassName, handleIcon: handleIcon, index: index, isDisabled: activeItem !== null && item.groupKey !== activeGroup, item: item, renderItem: renderItem }, item.id))) }) }), (0, jsx_runtime_1.jsx)(ps_drag_overlay_1.PsDragOverlay, { className: ps_drag_config_1.PsDragClasses.lifted, frames: ps_drag_config_1.PsDragListLiftFrames, children: activeItem !== null
69
+ return ((0, jsx_runtime_1.jsxs)(core_1.DndContext, { accessibility: { announcements, screenReaderInstructions: { draggable: instructions } }, collisionDetection: core_1.closestCenter, onDragCancel: () => setActiveId(null), onDragEnd: handleDragEnd, onDragStart: ({ active }) => setActiveId(String(active.id)), sensors: sensors, children: [(0, jsx_runtime_1.jsx)(sortable_1.SortableContext, { items: items.map((item) => item.id), strategy: isHorizontal ? sortable_1.horizontalListSortingStrategy : sortable_1.verticalListSortingStrategy, children: (0, jsx_runtime_1.jsx)("div", { className: className, role: role, children: items.map((item, index) => ((0, jsx_runtime_1.jsx)(PsSortableListRow, { className: itemClassName(item), getHandleLabel: getHandleLabel, handleClassName: handleClassName, handleIcon: handleIcon, index: index, isDisabled: activeItem !== null && item.groupKey !== activeGroup, item: item, renderItem: renderItem }, item.id))) }) }), (0, jsx_runtime_1.jsx)(ps_drag_overlay_1.PsDragOverlay, { className: ps_drag_config_1.PsDragClasses.lifted, frames: ps_drag_config_1.PsDragListLiftFrames, children: activeItem !== null
70
70
  ? renderItem({
71
71
  handle: ((0, jsx_runtime_1.jsx)(PsSortableListHandle, { className: handleClassName, icon: handleIcon, label: getHandleLabel(activeItem.label) })),
72
72
  index: activeIndex,
@@ -0,0 +1,10 @@
1
+ export declare const PsRatingDisplayConfig: {
2
+ readonly defaultMax: 5;
3
+ readonly filledClassName: "fill-current text-primary";
4
+ readonly emptyClassName: "text-ink/30 dark:text-white/30";
5
+ readonly iconClassNames: {
6
+ readonly large: "h-8 w-8";
7
+ readonly medium: "h-6 w-6";
8
+ readonly small: "h-4 w-4";
9
+ };
10
+ };
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsRatingDisplayConfig = void 0;
4
+ const ps_rating_display_size_1 = require("./ps-rating-display-size");
5
+ exports.PsRatingDisplayConfig = {
6
+ defaultMax: 5,
7
+ filledClassName: 'fill-current text-primary',
8
+ emptyClassName: 'text-ink/30 dark:text-white/30',
9
+ iconClassNames: {
10
+ [ps_rating_display_size_1.PsRatingDisplaySize.Large]: 'h-8 w-8',
11
+ [ps_rating_display_size_1.PsRatingDisplaySize.Medium]: 'h-6 w-6',
12
+ [ps_rating_display_size_1.PsRatingDisplaySize.Small]: 'h-4 w-4',
13
+ },
14
+ };
@@ -0,0 +1,5 @@
1
+ export declare enum PsRatingDisplaySize {
2
+ Large = "large",
3
+ Medium = "medium",
4
+ Small = "small"
5
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsRatingDisplaySize = void 0;
4
+ var PsRatingDisplaySize;
5
+ (function (PsRatingDisplaySize) {
6
+ PsRatingDisplaySize["Large"] = "large";
7
+ PsRatingDisplaySize["Medium"] = "medium";
8
+ PsRatingDisplaySize["Small"] = "small";
9
+ })(PsRatingDisplaySize || (exports.PsRatingDisplaySize = PsRatingDisplaySize = {}));
@@ -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,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
3
+ import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
4
+ import { PsRatingDisplayConfig } from '../constants/ps-rating-display-config';
5
+ import { PsRatingDisplaySize } from '../constants/ps-rating-display-size';
6
+ export const PsRatingDisplay = ({ className, label, max = PsRatingDisplayConfig.defaultMax, score, size = PsRatingDisplaySize.Medium, }) => (_jsx("div", { "aria-label": label, className: PallasoftClassNameService.merge('flex items-center gap-1', className), role: 'img', children: Array.from({ length: max }, (_, index) => index + 1).map((value) => (_jsx(PsIcon, { className: PallasoftClassNameService.merge(PsRatingDisplayConfig.iconClassNames[size], value <= score
7
+ ? PsRatingDisplayConfig.filledClassName
8
+ : PsRatingDisplayConfig.emptyClassName), iconKey: PsIconKey.Star }, value))) }));
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { DndContext, closestCenter, } from '@dnd-kit/core';
4
- import { SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
4
+ import { SortableContext, horizontalListSortingStrategy, useSortable, verticalListSortingStrategy, } from '@dnd-kit/sortable';
5
5
  import { CSS } from '@dnd-kit/utilities';
6
6
  import { PsDragClasses, PsDragConfig, PsDragListLiftFrames } from '../constants/ps-drag-config';
7
7
  import { usePsDragSensors } from '../hooks/use-ps-drag-sensors';
@@ -27,7 +27,7 @@ const PsSortableListRow = ({ className, handleClassName, handleIcon, getHandleLa
27
27
  transition: isReduced ? undefined : transition,
28
28
  }, children: rendered }));
29
29
  };
30
- export const PsSortableList = ({ announcements: texts, className, getHandleLabel, getItemClassName, handleClassName, handleIcon, instructions, role, items, onMove, renderItem, }) => {
30
+ export const PsSortableList = ({ announcements: texts, className, getHandleLabel, getItemClassName, handleClassName, handleIcon, instructions, isHorizontal = false, role, items, onMove, renderItem, }) => {
31
31
  const [activeId, setActiveId] = useState(null);
32
32
  const sensors = usePsDragSensors();
33
33
  const activeIndex = items.findIndex((item) => item.id === activeId);
@@ -63,7 +63,7 @@ export const PsSortableList = ({ announcements: texts, className, getHandleLabel
63
63
  onMove(String(active.id), from, to);
64
64
  };
65
65
  const activeGroup = activeItem === null ? undefined : activeItem.groupKey;
66
- return (_jsxs(DndContext, { accessibility: { announcements, screenReaderInstructions: { draggable: instructions } }, collisionDetection: closestCenter, onDragCancel: () => setActiveId(null), onDragEnd: handleDragEnd, onDragStart: ({ active }) => setActiveId(String(active.id)), sensors: sensors, children: [_jsx(SortableContext, { items: items.map((item) => item.id), strategy: verticalListSortingStrategy, children: _jsx("div", { className: className, role: role, children: items.map((item, index) => (_jsx(PsSortableListRow, { className: itemClassName(item), getHandleLabel: getHandleLabel, handleClassName: handleClassName, handleIcon: handleIcon, index: index, isDisabled: activeItem !== null && item.groupKey !== activeGroup, item: item, renderItem: renderItem }, item.id))) }) }), _jsx(PsDragOverlay, { className: PsDragClasses.lifted, frames: PsDragListLiftFrames, children: activeItem !== null
66
+ return (_jsxs(DndContext, { accessibility: { announcements, screenReaderInstructions: { draggable: instructions } }, collisionDetection: closestCenter, onDragCancel: () => setActiveId(null), onDragEnd: handleDragEnd, onDragStart: ({ active }) => setActiveId(String(active.id)), sensors: sensors, children: [_jsx(SortableContext, { items: items.map((item) => item.id), strategy: isHorizontal ? horizontalListSortingStrategy : verticalListSortingStrategy, children: _jsx("div", { className: className, role: role, children: items.map((item, index) => (_jsx(PsSortableListRow, { className: itemClassName(item), getHandleLabel: getHandleLabel, handleClassName: handleClassName, handleIcon: handleIcon, index: index, isDisabled: activeItem !== null && item.groupKey !== activeGroup, item: item, renderItem: renderItem }, item.id))) }) }), _jsx(PsDragOverlay, { className: PsDragClasses.lifted, frames: PsDragListLiftFrames, children: activeItem !== null
67
67
  ? renderItem({
68
68
  handle: (_jsx(PsSortableListHandle, { className: handleClassName, icon: handleIcon, label: getHandleLabel(activeItem.label) })),
69
69
  index: activeIndex,
@@ -0,0 +1,11 @@
1
+ import { PsRatingDisplaySize } from './ps-rating-display-size';
2
+ export const PsRatingDisplayConfig = {
3
+ defaultMax: 5,
4
+ filledClassName: 'fill-current text-primary',
5
+ emptyClassName: 'text-ink/30 dark:text-white/30',
6
+ iconClassNames: {
7
+ [PsRatingDisplaySize.Large]: 'h-8 w-8',
8
+ [PsRatingDisplaySize.Medium]: 'h-6 w-6',
9
+ [PsRatingDisplaySize.Small]: 'h-4 w-4',
10
+ },
11
+ };
@@ -0,0 +1,6 @@
1
+ export var PsRatingDisplaySize;
2
+ (function (PsRatingDisplaySize) {
3
+ PsRatingDisplaySize["Large"] = "large";
4
+ PsRatingDisplaySize["Medium"] = "medium";
5
+ PsRatingDisplaySize["Small"] = "small";
6
+ })(PsRatingDisplaySize || (PsRatingDisplaySize = {}));
@@ -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';
@@ -147,3 +154,5 @@ export * from './hooks/use-ps-drag-sensors';
147
154
  export * from './hooks/use-ps-reduced-motion';
148
155
  export * from './components/ps-divider';
149
156
  export * from './models/ps-divider-orientation';
157
+ export * from './components/ps-rating-display';
158
+ export * from './constants/ps-rating-display-size';
@@ -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';
@@ -147,3 +154,5 @@ export * from './hooks/use-ps-drag-sensors';
147
154
  export * from './hooks/use-ps-reduced-motion';
148
155
  export * from './components/ps-divider';
149
156
  export * from './models/ps-divider-orientation';
157
+ export * from './components/ps-rating-display';
158
+ export * from './constants/ps-rating-display-size';
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);
@@ -163,3 +170,5 @@ __exportStar(require("./hooks/use-ps-drag-sensors"), exports);
163
170
  __exportStar(require("./hooks/use-ps-reduced-motion"), exports);
164
171
  __exportStar(require("./components/ps-divider"), exports);
165
172
  __exportStar(require("./models/ps-divider-orientation"), exports);
173
+ __exportStar(require("./components/ps-rating-display"), exports);
174
+ __exportStar(require("./constants/ps-rating-display-size"), 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 });
@@ -27,6 +27,7 @@ export interface PsSortableListProps<TItem extends PsSortableListItem> {
27
27
  handleIcon?: JSX.Element;
28
28
  instructions: string;
29
29
  items: TItem[];
30
+ isHorizontal?: boolean;
30
31
  onMove: (itemId: string, fromIndex: number, toIndex: number) => void;
31
32
  renderItem: (options: PsSortableListRenderOptions<TItem>) => JSX.Element | null;
32
33
  }
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.29.0",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/esm/index.js",