@pallasoft/ps-core-components 1.0.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 (57) hide show
  1. package/LICENSE +32 -0
  2. package/README.md +10 -0
  3. package/dist/components/ps-accordion.d.ts +10 -0
  4. package/dist/components/ps-accordion.js +41 -0
  5. package/dist/components/ps-action-tile.d.ts +8 -0
  6. package/dist/components/ps-action-tile.js +11 -0
  7. package/dist/components/ps-audio-spectrum.d.ts +5 -0
  8. package/dist/components/ps-audio-spectrum.js +13 -0
  9. package/dist/components/ps-badge.d.ts +7 -0
  10. package/dist/components/ps-badge.js +14 -0
  11. package/dist/components/ps-button.d.ts +11 -0
  12. package/dist/components/ps-button.js +19 -0
  13. package/dist/components/ps-card.d.ts +11 -0
  14. package/dist/components/ps-card.js +18 -0
  15. package/dist/components/ps-draggable-grid.d.ts +19 -0
  16. package/dist/components/ps-draggable-grid.js +56 -0
  17. package/dist/components/ps-empty-state.d.ts +17 -0
  18. package/dist/components/ps-empty-state.js +13 -0
  19. package/dist/components/ps-fab.d.ts +5 -0
  20. package/dist/components/ps-fab.js +13 -0
  21. package/dist/components/ps-infinite-scroll.d.ts +10 -0
  22. package/dist/components/ps-infinite-scroll.js +12 -0
  23. package/dist/components/ps-list-tile.d.ts +12 -0
  24. package/dist/components/ps-list-tile.js +15 -0
  25. package/dist/components/ps-metric-card.d.ts +9 -0
  26. package/dist/components/ps-metric-card.js +7 -0
  27. package/dist/components/ps-pill.d.ts +7 -0
  28. package/dist/components/ps-pill.js +14 -0
  29. package/dist/components/ps-reflective-app-shell.d.ts +10 -0
  30. package/dist/components/ps-reflective-app-shell.js +7 -0
  31. package/dist/components/ps-section-header.d.ts +8 -0
  32. package/dist/components/ps-section-header.js +6 -0
  33. package/dist/components/ps-skeleton.d.ts +4 -0
  34. package/dist/components/ps-skeleton.js +7 -0
  35. package/dist/components/ps-spinner.d.ts +6 -0
  36. package/dist/components/ps-spinner.js +12 -0
  37. package/dist/components/ps-theme-toggle.d.ts +6 -0
  38. package/dist/components/ps-theme-toggle.js +11 -0
  39. package/dist/components/ps-voice-orb.d.ts +7 -0
  40. package/dist/components/ps-voice-orb.js +40 -0
  41. package/dist/hooks/use-ps-audio-spectrum.d.ts +9 -0
  42. package/dist/hooks/use-ps-audio-spectrum.js +66 -0
  43. package/dist/hooks/use-ps-exit-transition.d.ts +5 -0
  44. package/dist/hooks/use-ps-exit-transition.js +34 -0
  45. package/dist/hooks/use-ps-hide-on-scroll.d.ts +4 -0
  46. package/dist/hooks/use-ps-hide-on-scroll.js +29 -0
  47. package/dist/hooks/use-ps-invisible-recaptcha.d.ts +27 -0
  48. package/dist/hooks/use-ps-invisible-recaptcha.js +93 -0
  49. package/dist/hooks/use-ps-route-loader-data.d.ts +1 -0
  50. package/dist/hooks/use-ps-route-loader-data.js +16 -0
  51. package/dist/hooks/use-ps-viewport.d.ts +2 -0
  52. package/dist/hooks/use-ps-viewport.js +22 -0
  53. package/dist/index.d.ts +26 -0
  54. package/dist/index.js +42 -0
  55. package/dist/interfaces/ps-component-props.d.ts +7 -0
  56. package/dist/interfaces/ps-component-props.js +2 -0
  57. package/package.json +75 -0
package/LICENSE ADDED
@@ -0,0 +1,32 @@
1
+ Pallasoft Proprietary License
2
+
3
+ Version 1.0, 23-Jan-2024
4
+
5
+ 1. Grant of License:
6
+ Pallasoft grants you a non-exclusive, non-transferable, revocable license to use the software, solely for internal business purposes.
7
+
8
+ 2. Restrictions:
9
+ You may not:
10
+ a. Distribute or sublicense the software to third parties.
11
+ b. Reverse engineer, decompile, or disassemble the software.
12
+ c. Remove or alter any proprietary notices or labels on the software.
13
+
14
+ 3. Ownership:
15
+ Pallasoft retains all right, title, and interest in and to the software, including all intellectual property rights.
16
+
17
+ 4. Confidentiality:
18
+ You agree to keep the software confidential and not disclose it to third parties.
19
+
20
+ 5. Support and Updates:
21
+ This license does not entitle you to support or updates. Pallasoft may, at its discretion, provide support and updates.
22
+
23
+ 6. Termination:
24
+ This license is effective until terminated. Pallasoft may terminate the license at any time if you breach any terms. Upon termination, you must cease using the software.
25
+
26
+ 7. Governing Law:
27
+ This license is governed by the Canadian Laws. Any disputes arising out of this license shall be resolved in the courts of Canada.
28
+
29
+ 8. Contact Information:
30
+ For inquiries regarding this license, contact operations@pallasoft.com.
31
+
32
+ [End of License]
package/README.md ADDED
@@ -0,0 +1,10 @@
1
+ # @pallasoft/ps-core-components
2
+
3
+ Shared Pallasoft core components package.
4
+
5
+ ## Scripts
6
+
7
+ - `npm run build` compiles the package into `dist`.
8
+ - `npm run lint` runs ESLint using the shared package baseline.
9
+ - `npm run format:check` checks Prettier formatting.
10
+ - `npm run release` publishes through semantic-release.
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface PsAccordionProps {
3
+ children: ReactNode;
4
+ className?: string;
5
+ isOpen: boolean;
6
+ label: ReactNode;
7
+ onToggle: () => void;
8
+ trailing?: ReactNode;
9
+ }
10
+ export declare const PsAccordion: ({ children, className, isOpen, label, onToggle, trailing, }: PsAccordionProps) => import("react").JSX.Element;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PsAccordion = void 0;
37
+ const jsx_runtime_1 = require("react/jsx-runtime");
38
+ const AccordionPrimitive = __importStar(require("@radix-ui/react-accordion"));
39
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
40
+ const PsAccordion = ({ children, className, isOpen, label, onToggle, trailing, }) => ((0, jsx_runtime_1.jsx)(AccordionPrimitive.Root, { type: 'single', collapsible: true, value: isOpen ? 'open' : '', onValueChange: onToggle, className: ps_design_system_1.PallasoftClassNameService.merge('overflow-hidden rounded-2xl border border-line/70 bg-mist/60', className), children: (0, jsx_runtime_1.jsxs)(AccordionPrimitive.Item, { value: 'open', children: [(0, jsx_runtime_1.jsx)(AccordionPrimitive.Header, { children: (0, jsx_runtime_1.jsxs)(AccordionPrimitive.Trigger, { className: 'ps-focus-ring flex w-full items-center justify-between gap-3 px-4 py-3.5', children: [(0, jsx_runtime_1.jsx)("span", { className: 'text-body-2 font-semibold text-ink', children: label }), trailing] }) }), (0, jsx_runtime_1.jsx)(AccordionPrimitive.Content, { className: 'ps-accordion-content', children: (0, jsx_runtime_1.jsx)("div", { className: 'border-t border-line/70 p-4', children: children }) })] }) }));
41
+ exports.PsAccordion = PsAccordion;
@@ -0,0 +1,8 @@
1
+ import type { ButtonHTMLAttributes, ReactNode } from 'react';
2
+ export interface PsActionTileProps extends ButtonHTMLAttributes<HTMLButtonElement> {
3
+ description?: string;
4
+ icon?: ReactNode;
5
+ label: string;
6
+ variant?: 'default' | 'primary';
7
+ }
8
+ export declare const PsActionTile: ({ className, description, icon, label, type, variant, ...props }: PsActionTileProps) => import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsActionTile = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const variantClassNames = {
7
+ default: 'ps-action-tile',
8
+ primary: 'ps-action-tile ps-action-tile-primary',
9
+ };
10
+ const PsActionTile = ({ className, description, icon, label, type = 'button', variant = 'default', ...props }) => ((0, jsx_runtime_1.jsxs)("button", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring w-full', variantClassNames[variant], className), type: type, ...props, children: [(0, jsx_runtime_1.jsxs)("span", { className: 'min-w-0', children: [(0, jsx_runtime_1.jsx)("span", { className: 'block break-words', children: label }), description ? ((0, jsx_runtime_1.jsx)("span", { className: ps_design_system_1.PallasoftClassNameService.merge('mt-0.5 block text-xs font-medium leading-5', variant === 'primary' ? 'text-white/75' : 'text-ink/50'), children: description })) : null] }), icon ? ((0, jsx_runtime_1.jsx)("span", { className: ps_design_system_1.PallasoftClassNameService.merge('flex h-9 w-9 shrink-0 items-center justify-center rounded-lg', variant === 'primary' ? 'bg-white/15 text-white' : 'ps-icon-surface'), children: icon })) : null] }));
11
+ exports.PsActionTile = PsActionTile;
@@ -0,0 +1,5 @@
1
+ export interface PsAudioSpectrumProps {
2
+ className?: string;
3
+ levels: number[];
4
+ }
5
+ export declare const PsAudioSpectrum: (props: PsAudioSpectrumProps) => import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsAudioSpectrum = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const MinBarHeightPercent = 10;
7
+ const PsAudioSpectrum = (props) => {
8
+ return ((0, jsx_runtime_1.jsx)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('flex h-full w-full items-end gap-2', props.className), children: props.levels.map((level, index) => {
9
+ const heightPercent = MinBarHeightPercent + Math.max(0, Math.min(1, level)) * (100 - MinBarHeightPercent);
10
+ return ((0, jsx_runtime_1.jsx)("span", { className: 'min-w-0 flex-1 rounded-full bg-[linear-gradient(180deg,_rgb(var(--ps-color-primary-rgb)/0.25)_0%,_rgb(var(--ps-color-primary-rgb))_100%)] transition-[height] duration-100 ease-out', style: { height: `${heightPercent}%` } }, index));
11
+ }) }));
12
+ };
13
+ exports.PsAudioSpectrum = PsAudioSpectrum;
@@ -0,0 +1,7 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface PsBadgeProps {
3
+ children: ReactNode;
4
+ className?: string;
5
+ tone?: 'danger' | 'neutral' | 'primary' | 'success' | 'warning';
6
+ }
7
+ export declare const PsBadge: ({ children, className, tone }: PsBadgeProps) => import("react").JSX.Element;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsBadge = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const toneClassNames = {
7
+ danger: 'bg-danger-soft text-danger dark:bg-rose-500/18 dark:text-rose-100',
8
+ neutral: 'bg-slate-100 text-slate-700 dark:bg-white/10 dark:text-slate-100',
9
+ primary: 'bg-primary-soft text-primary dark:bg-primary/22 dark:text-white',
10
+ success: 'bg-success-soft text-emerald-700 dark:bg-emerald-400/18 dark:text-emerald-50',
11
+ warning: 'bg-warning-soft text-amber-800 dark:bg-amber-400/18 dark:text-amber-50',
12
+ };
13
+ const PsBadge = ({ children, className, tone = 'neutral' }) => ((0, jsx_runtime_1.jsx)("span", { className: ps_design_system_1.PallasoftClassNameService.merge('inline-flex min-h-8 min-w-[5.5rem] w-fit items-center justify-center rounded-full px-5 py-1 text-center text-[0.72rem] font-black capitalize tracking-[0.01em]', toneClassNames[tone], className), children: children }));
14
+ exports.PsBadge = PsBadge;
@@ -0,0 +1,11 @@
1
+ import type { ButtonHTMLAttributes, ReactNode } from 'react';
2
+ export interface PsButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
3
+ children: ReactNode;
4
+ isLoading?: boolean;
5
+ leadingIcon?: ReactNode;
6
+ loadingLabel?: string;
7
+ size?: 'compact' | 'default' | 'large';
8
+ trailingIcon?: ReactNode;
9
+ variant?: 'danger' | 'ghost' | 'primary' | 'secondary' | 'surface';
10
+ }
11
+ export declare const PsButton: ({ children, className, disabled, isLoading, leadingIcon, loadingLabel, size, trailingIcon, type, variant, ...props }: PsButtonProps) => import("react").JSX.Element;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsButton = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const sizeClassNames = {
7
+ compact: 'min-h-9 px-3 py-2 text-sm',
8
+ default: 'min-h-10 px-4 py-2.5 text-sm',
9
+ large: 'min-h-11 px-5 py-3 text-sm',
10
+ };
11
+ const variantClassNames = {
12
+ danger: 'border-danger bg-danger text-white hover:opacity-90',
13
+ ghost: 'border-transparent bg-transparent text-ink/60 hover:text-ink',
14
+ primary: 'border-primary bg-primary text-white',
15
+ secondary: 'ps-liquid ps-liquid-control bg-white/72 text-ink hover:border-primary/30 hover:text-primary dark:border-white/16 dark:bg-white/12 dark:hover:text-white',
16
+ surface: 'ps-liquid ps-liquid-control bg-white/72 text-ink hover:border-primary/30 dark:border-white/16 dark:bg-white/12',
17
+ };
18
+ const PsButton = ({ children, className, disabled, isLoading = false, leadingIcon, loadingLabel, size = 'default', trailingIcon, type = 'button', variant = 'secondary', ...props }) => ((0, jsx_runtime_1.jsxs)("button", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring inline-flex max-w-full items-center justify-center gap-2 rounded-full border text-center font-semibold disabled:cursor-not-allowed disabled:opacity-50', sizeClassNames[size], variantClassNames[variant], className), "aria-busy": isLoading || undefined, disabled: disabled || isLoading, type: type, ...props, children: [isLoading ? ((0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', className: 'h-4 w-4 shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent' })) : leadingIcon ? ((0, jsx_runtime_1.jsx)("span", { className: 'shrink-0', children: leadingIcon })) : null, (0, jsx_runtime_1.jsx)("span", { className: 'truncate', children: isLoading && loadingLabel ? loadingLabel : children }), !isLoading && trailingIcon ? (0, jsx_runtime_1.jsx)("span", { className: 'shrink-0', children: trailingIcon }) : null] }));
19
+ exports.PsButton = PsButton;
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface PsCardProps {
3
+ children: ReactNode;
4
+ className?: string;
5
+ density?: 'compact' | 'normal' | 'spacious';
6
+ eyebrow?: string;
7
+ title?: string;
8
+ trailing?: ReactNode;
9
+ variant?: 'default' | 'elevated' | 'glass' | 'muted';
10
+ }
11
+ export declare const PsCard: ({ children, className, density, eyebrow, title, trailing, variant, }: PsCardProps) => import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsCard = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const densityClassNames = {
7
+ compact: 'p-3',
8
+ normal: 'p-4',
9
+ spacious: 'p-5',
10
+ };
11
+ const variantClassNames = {
12
+ default: 'ps-card',
13
+ elevated: 'ps-card',
14
+ glass: 'ps-glass',
15
+ muted: 'ps-card-muted',
16
+ };
17
+ const PsCard = ({ children, className, density = 'normal', eyebrow, title, trailing, variant = 'default', }) => ((0, jsx_runtime_1.jsxs)("section", { className: ps_design_system_1.PallasoftClassNameService.merge('min-w-0 overflow-hidden rounded-ps-card', variantClassNames[variant], densityClassNames[density], className), children: [title ? ((0, jsx_runtime_1.jsxs)("div", { className: 'mb-3 flex min-w-0 items-start justify-between gap-3', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0', children: [eyebrow ? ((0, jsx_runtime_1.jsx)("p", { className: 'ps-eyebrow break-words', children: eyebrow })) : null, (0, jsx_runtime_1.jsx)("h2", { className: 'mt-1 break-words text-base font-bold text-ink', children: title })] }), trailing ? (0, jsx_runtime_1.jsx)("div", { className: 'shrink-0', children: trailing }) : null] })) : null, children] }));
18
+ exports.PsCard = PsCard;
@@ -0,0 +1,19 @@
1
+ import type { JSX } from 'react';
2
+ export interface PsDraggableGridItem {
3
+ id: string;
4
+ label: string;
5
+ }
6
+ export interface PsDraggableGridRenderOptions<TItem extends PsDraggableGridItem> {
7
+ handle: JSX.Element;
8
+ index: number;
9
+ isDragging: boolean;
10
+ item: TItem;
11
+ }
12
+ export interface PsDraggableGridProps<TItem extends PsDraggableGridItem> {
13
+ getItemClassName?: (item: TItem) => string;
14
+ items: TItem[];
15
+ onReorder: (items: TItem[]) => void;
16
+ onReorderEnd?: (items: TItem[]) => void;
17
+ renderItem: (options: PsDraggableGridRenderOptions<TItem>) => JSX.Element | null;
18
+ }
19
+ export declare const PsDraggableGrid: <TItem extends PsDraggableGridItem>({ getItemClassName, items, onReorder, onReorderEnd, renderItem, }: PsDraggableGridProps<TItem>) => JSX.Element;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsDraggableGrid = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const moveItem = (items, fromIndex, toIndex) => {
6
+ const nextItems = [...items];
7
+ const [movingItem] = nextItems.splice(fromIndex, 1);
8
+ nextItems.splice(toIndex, 0, movingItem);
9
+ return nextItems;
10
+ };
11
+ let draggedItemId;
12
+ let latestItems = [];
13
+ const DragHandleIcon = () => ((0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', className: 'grid grid-cols-2 gap-[0.1875rem]', children: Array.from({ length: 6 }).map((_, index) => ((0, jsx_runtime_1.jsx)("span", { className: 'h-0.5 w-0.5 rounded-full bg-current' }, index))) }));
14
+ const PsDraggableGrid = ({ getItemClassName, items, onReorder, onReorderEnd, renderItem, }) => {
15
+ latestItems = items;
16
+ const finishDrag = () => {
17
+ if (!draggedItemId)
18
+ return;
19
+ draggedItemId = undefined;
20
+ onReorderEnd?.(latestItems);
21
+ };
22
+ const moveDraggedItemTo = (targetItemId) => {
23
+ if (!draggedItemId || draggedItemId === targetItemId)
24
+ return;
25
+ const currentItems = latestItems;
26
+ const fromIndex = currentItems.findIndex((item) => item.id === draggedItemId);
27
+ const toIndex = currentItems.findIndex((item) => item.id === targetItemId);
28
+ if (fromIndex < 0 || toIndex < 0 || fromIndex === toIndex)
29
+ return;
30
+ const nextItems = moveItem(currentItems, fromIndex, toIndex);
31
+ latestItems = nextItems;
32
+ onReorder(nextItems);
33
+ };
34
+ return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: items.map((item, index) => {
35
+ const isDragging = false;
36
+ const handle = ((0, jsx_runtime_1.jsx)("button", { "aria-label": `Move ${item.label}`, className: 'ps-focus-ring inline-flex h-6 w-6 shrink-0 cursor-grab items-center justify-center rounded-full text-slate-400/55 opacity-45 transition hover:bg-white/36 hover:text-slate-600 hover:opacity-90 active:cursor-grabbing dark:text-slate-300/45 dark:hover:bg-white/8 dark:hover:text-slate-100', draggable: true, onDragEnd: finishDrag, onDragStart: (event) => {
37
+ event.dataTransfer.effectAllowed = 'move';
38
+ event.dataTransfer.setData('text/plain', item.id);
39
+ draggedItemId = item.id;
40
+ }, title: `Move ${item.label}`, type: 'button', children: (0, jsx_runtime_1.jsx)(DragHandleIcon, {}) }));
41
+ const renderedItem = renderItem({ handle, index, isDragging, item });
42
+ if (!renderedItem)
43
+ return null;
44
+ return ((0, jsx_runtime_1.jsx)("div", { className: `ps-page-enter min-w-0 rounded-[1.7rem] transition ${getItemClassName?.(item) ?? ''} ${isDragging ? 'scale-[0.985] opacity-70' : ''}`, onDragOver: (event) => {
45
+ if (!draggedItemId)
46
+ return;
47
+ event.preventDefault();
48
+ event.dataTransfer.dropEffect = 'move';
49
+ moveDraggedItemTo(item.id);
50
+ }, onDrop: (event) => {
51
+ event.preventDefault();
52
+ finishDrag();
53
+ }, children: renderedItem }, item.id));
54
+ }) }));
55
+ };
56
+ exports.PsDraggableGrid = PsDraggableGrid;
@@ -0,0 +1,17 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface PsEmptyStateAction {
3
+ href: string;
4
+ label: string;
5
+ variant?: 'primary' | 'secondary';
6
+ }
7
+ export interface PsEmptyStateProps {
8
+ actions?: PsEmptyStateAction[];
9
+ badge?: string;
10
+ className?: string;
11
+ detail?: string;
12
+ description: string;
13
+ eyebrow?: string;
14
+ icon?: ReactNode;
15
+ title: string;
16
+ }
17
+ export declare const PsEmptyState: ({ actions, badge, className, detail, description, eyebrow, icon, title, }: PsEmptyStateProps) => import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsEmptyState = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const getActionClassName = (variant = 'secondary') => {
7
+ if (variant === 'primary') {
8
+ return 'border-primary bg-primary text-white hover:bg-primary-dark';
9
+ }
10
+ return 'border-line bg-surface text-ink hover:border-primary/40 hover:text-primary';
11
+ };
12
+ const PsEmptyState = ({ actions = [], badge, className, detail, description, eyebrow, icon, title, }) => ((0, jsx_runtime_1.jsx)("main", { className: ps_design_system_1.PallasoftClassNameService.merge('relative flex min-h-screen items-center justify-center bg-mist px-4 py-10 text-ink', className), children: (0, jsx_runtime_1.jsx)("section", { className: 'ps-card relative w-full max-w-2xl p-6 sm:p-8', children: (0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col gap-5 sm:flex-row sm:items-start', children: [icon ? ((0, jsx_runtime_1.jsx)("div", { className: 'ps-icon-surface h-14 w-14', children: icon })) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0 flex-1', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-wrap items-center gap-2', children: [eyebrow ? ((0, jsx_runtime_1.jsx)("p", { className: 'text-xs font-semibold uppercase tracking-[0.22em] text-primary', children: eyebrow })) : null, eyebrow && badge ? (0, jsx_runtime_1.jsx)("span", { className: 'h-1 w-1 rounded-full bg-line' }) : null, badge ? ((0, jsx_runtime_1.jsx)("p", { className: 'text-xs font-semibold uppercase tracking-[0.22em] text-ink/40', children: badge })) : null] }), (0, jsx_runtime_1.jsx)("h1", { className: 'mt-3 text-3xl font-semibold tracking-tight sm:text-4xl', children: title }), (0, jsx_runtime_1.jsx)("p", { className: 'mt-3 text-sm leading-6 text-ink/60', children: description }), detail ? ((0, jsx_runtime_1.jsx)("p", { className: 'mt-4 rounded-2xl border border-line bg-mist px-4 py-3 text-left text-xs leading-5 text-ink/60', children: detail })) : null, actions.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: 'mt-6 flex flex-col gap-2 sm:flex-row', children: actions.map((action) => ((0, jsx_runtime_1.jsx)("a", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring ps-pressable inline-flex items-center justify-center rounded-ps-control border px-4 py-3 text-sm font-semibold', getActionClassName(action.variant)), href: action.href, children: action.label }, `${action.href}-${action.label}`))) })) : null] })] }) }) }));
13
+ exports.PsEmptyState = PsEmptyState;
@@ -0,0 +1,5 @@
1
+ import type { PsButtonProps } from './ps-button';
2
+ export interface PsFabProps extends PsButtonProps {
3
+ position?: 'bottom-center' | 'bottom-left' | 'bottom-right';
4
+ }
5
+ export declare const PsFab: ({ className, position, ...props }: PsFabProps) => import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsFab = 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_button_1 = require("./ps-button");
7
+ const positionClassNames = {
8
+ 'bottom-right': 'bottom-6 right-6',
9
+ 'bottom-left': 'bottom-6 left-6',
10
+ 'bottom-center': 'bottom-6 left-1/2 -translate-x-1/2',
11
+ };
12
+ const PsFab = ({ className, position = 'bottom-right', ...props }) => ((0, jsx_runtime_1.jsx)(ps_button_1.PsButton, { size: 'large', variant: 'primary', className: ps_design_system_1.PallasoftClassNameService.merge('fixed z-docked h-14 w-14 shrink-0 !px-0 shadow-lg', positionClassNames[position], className), ...props }));
13
+ exports.PsFab = PsFab;
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface PsInfiniteScrollProps {
3
+ children: ReactNode;
4
+ className?: string;
5
+ dataLength: number;
6
+ hasMore: boolean;
7
+ loader?: ReactNode;
8
+ next: () => void;
9
+ }
10
+ export declare const PsInfiniteScroll: ({ children, className, dataLength, hasMore, loader, next, }: PsInfiniteScrollProps) => import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PsInfiniteScroll = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_infinite_scroll_component_1 = __importDefault(require("react-infinite-scroll-component"));
9
+ const ps_spinner_1 = require("./ps-spinner");
10
+ const defaultLoader = ((0, jsx_runtime_1.jsx)("div", { className: 'flex w-full justify-center py-8', children: (0, jsx_runtime_1.jsx)(ps_spinner_1.PsSpinner, {}) }));
11
+ const PsInfiniteScroll = ({ children, className, dataLength, hasMore, loader, next, }) => ((0, jsx_runtime_1.jsx)(react_infinite_scroll_component_1.default, { className: className, dataLength: dataLength, hasMore: hasMore, loader: loader ?? defaultLoader, next: next, children: children }));
12
+ exports.PsInfiniteScroll = PsInfiniteScroll;
@@ -0,0 +1,12 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface PsListTileProps {
3
+ children?: ReactNode;
4
+ className?: string;
5
+ density?: 'compact' | 'normal';
6
+ eyebrow?: string;
7
+ isPrivateTitle?: boolean;
8
+ subtitle?: string;
9
+ title: ReactNode;
10
+ variant?: 'card' | 'row';
11
+ }
12
+ export declare const PsListTile: ({ children, className, density, eyebrow, isPrivateTitle, subtitle, title, variant, }: PsListTileProps) => import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsListTile = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const densityClassNames = {
7
+ compact: 'py-2',
8
+ normal: 'py-2.5',
9
+ };
10
+ const variantClassNames = {
11
+ card: 'ps-surface px-3',
12
+ row: 'border-b border-line/70 px-0 last:border-b-0',
13
+ };
14
+ const PsListTile = ({ children, className, density = 'normal', eyebrow, isPrivateTitle = false, subtitle, title, variant = 'row', }) => ((0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('min-w-0 overflow-hidden', variantClassNames[variant], densityClassNames[density], className), children: [eyebrow ? ((0, jsx_runtime_1.jsx)("p", { className: 'ps-eyebrow break-words', children: eyebrow })) : null, (0, jsx_runtime_1.jsx)("p", { className: ps_design_system_1.PallasoftClassNameService.merge('break-words text-sm font-semibold text-ink', eyebrow && 'mt-1', isPrivateTitle && 'private-number'), children: title }), subtitle ? (0, jsx_runtime_1.jsx)("p", { className: 'mt-1 break-words text-sm leading-5 text-slate-500', children: subtitle }) : null, children] }));
15
+ exports.PsListTile = PsListTile;
@@ -0,0 +1,9 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface PsMetricCardProps {
3
+ changeLabel?: string;
4
+ className?: string;
5
+ icon?: ReactNode;
6
+ label: string;
7
+ value: ReactNode;
8
+ }
9
+ export declare const PsMetricCard: ({ changeLabel, className, icon, label, value, }: PsMetricCardProps) => import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsMetricCard = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const PsMetricCard = ({ changeLabel, className, icon, label, value, }) => ((0, jsx_runtime_1.jsx)("section", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-metric-card min-w-0 p-3.5 sm:p-4', className), children: (0, jsx_runtime_1.jsxs)("div", { className: 'flex items-start justify-between gap-3', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0', children: [(0, jsx_runtime_1.jsx)("p", { className: 'ps-eyebrow truncate', children: label }), (0, jsx_runtime_1.jsx)("p", { className: 'mt-2 truncate text-2xl font-semibold text-ink', children: value }), changeLabel ? (0, jsx_runtime_1.jsx)("p", { className: 'mt-1 text-sm text-slate-500', children: changeLabel }) : null] }), icon ? ((0, jsx_runtime_1.jsx)("div", { className: 'flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-primary-soft text-primary', children: icon })) : null] }) }));
7
+ exports.PsMetricCard = PsMetricCard;
@@ -0,0 +1,7 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface PsPillProps {
3
+ children: ReactNode;
4
+ className?: string;
5
+ tone?: 'danger' | 'neutral' | 'primary' | 'success' | 'warning';
6
+ }
7
+ export declare const PsPill: ({ children, className, tone }: PsPillProps) => import("react").JSX.Element;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsPill = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const toneClassNames = {
7
+ danger: 'bg-rose-50 text-rose-700 ring-rose-100 dark:bg-rose-500/18 dark:text-rose-100 dark:ring-rose-300/22',
8
+ neutral: 'bg-slate-100 text-slate-700 ring-slate-200 dark:bg-white/10 dark:text-slate-100 dark:ring-white/12',
9
+ primary: 'bg-primary-soft text-primary ring-primary/12 dark:bg-primary/22 dark:text-white dark:ring-primary/32',
10
+ success: 'bg-emerald-50 text-emerald-700 ring-emerald-100 dark:bg-emerald-400/18 dark:text-emerald-50 dark:ring-emerald-200/22',
11
+ warning: 'bg-amber-50 text-amber-800 ring-amber-100 dark:bg-amber-400/18 dark:text-amber-50 dark:ring-amber-200/24',
12
+ };
13
+ const PsPill = ({ children, className, tone = 'neutral' }) => ((0, jsx_runtime_1.jsx)("span", { className: ps_design_system_1.PallasoftClassNameService.merge('inline-flex min-h-8 min-w-[5.5rem] max-w-full items-center justify-center rounded-full px-5 py-1 text-center text-[0.72rem] font-black capitalize tracking-[0.01em] ring-1 ring-inset', toneClassNames[tone], className), children: (0, jsx_runtime_1.jsx)("span", { className: 'min-w-0 break-words', children: children }) }));
14
+ exports.PsPill = PsPill;
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface PsReflectiveAppShellProps {
3
+ children: ReactNode;
4
+ className?: string;
5
+ contentClassName?: string;
6
+ frameClassName?: string;
7
+ sidebar: ReactNode;
8
+ topbar: ReactNode;
9
+ }
10
+ export declare const PsReflectiveAppShell: ({ children, className, contentClassName, frameClassName, sidebar, topbar, }: PsReflectiveAppShellProps) => import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsReflectiveAppShell = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const PsReflectiveAppShell = ({ children, className, contentClassName, frameClassName, sidebar, topbar, }) => ((0, jsx_runtime_1.jsx)("section", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-reflective-frame min-h-screen text-ink lg:px-3 lg:py-3', className), children: (0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-liquid-strong mx-auto grid h-screen min-h-0 overflow-hidden border-line/70 bg-white/70 backdrop-blur-2xl dark:border-white/10 dark:bg-surface/70 lg:h-[calc(100vh-1.5rem)] lg:max-w-[1720px] lg:grid-cols-[17rem_minmax(0,1fr)] lg:rounded-[1.5rem] lg:border', frameClassName), children: [sidebar, (0, jsx_runtime_1.jsxs)("div", { className: 'flex min-h-0 min-w-0 flex-col bg-white/32 dark:bg-white/[0.02]', children: [topbar, (0, jsx_runtime_1.jsx)("main", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-scrollbar min-h-0 flex-1 overflow-y-auto px-4 py-5 ps-page-enter sm:px-5 lg:px-6 xl:px-7', contentClassName), children: children })] })] }) }));
7
+ exports.PsReflectiveAppShell = PsReflectiveAppShell;
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface PsSectionHeaderProps {
3
+ description?: string;
4
+ eyebrow?: string;
5
+ title: string;
6
+ trailing?: ReactNode;
7
+ }
8
+ export declare const PsSectionHeader: ({ description, eyebrow, title, trailing }: PsSectionHeaderProps) => import("react").JSX.Element;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsSectionHeader = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const PsSectionHeader = ({ description, eyebrow, title, trailing }) => ((0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col justify-between gap-3 lg:flex-row lg:items-end', children: [(0, jsx_runtime_1.jsxs)("div", { children: [eyebrow ? ((0, jsx_runtime_1.jsx)("p", { className: 'text-[0.68rem] font-semibold uppercase tracking-[0.22em] text-slate-400', children: eyebrow })) : null, (0, jsx_runtime_1.jsx)("h1", { className: 'text-2xl font-extrabold text-ink', children: title }), description ? (0, jsx_runtime_1.jsx)("p", { className: 'mt-2 max-w-3xl text-sm leading-6 text-slate-500', children: description }) : null] }), trailing] }));
6
+ exports.PsSectionHeader = PsSectionHeader;
@@ -0,0 +1,4 @@
1
+ export interface PsSkeletonProps {
2
+ className?: string;
3
+ }
4
+ export declare const PsSkeleton: ({ className }: PsSkeletonProps) => import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsSkeleton = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const PsSkeleton = ({ className }) => ((0, jsx_runtime_1.jsx)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('animate-pulse rounded-full bg-mist', className) }));
7
+ exports.PsSkeleton = PsSkeleton;
@@ -0,0 +1,6 @@
1
+ import type { HTMLAttributes } from 'react';
2
+ export interface PsSpinnerProps extends HTMLAttributes<HTMLDivElement> {
3
+ label?: string;
4
+ size?: 'default' | 'large' | 'small';
5
+ }
6
+ export declare const PsSpinner: ({ className, label, size, ...props }: PsSpinnerProps) => import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsSpinner = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const sizeClassNames = {
7
+ small: 'h-4 w-4 border-2',
8
+ default: 'h-6 w-6 border-2',
9
+ large: 'h-8 w-8 border-[3px]',
10
+ };
11
+ const PsSpinner = ({ className, label = 'Loading', size = 'default', ...props }) => ((0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('inline-flex items-center justify-center', className), ...props, children: [(0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', className: ps_design_system_1.PallasoftClassNameService.merge('shrink-0 animate-spin rounded-full border-current border-t-transparent text-primary', sizeClassNames[size]) }), (0, jsx_runtime_1.jsx)("span", { className: 'sr-only', children: label })] }));
12
+ exports.PsSpinner = PsSpinner;
@@ -0,0 +1,6 @@
1
+ import { PallasoftThemeMode } from '@pallasoft/ps-design-system';
2
+ import type { ButtonHTMLAttributes } from 'react';
3
+ export interface PsThemeToggleProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
4
+ mode: PallasoftThemeMode;
5
+ }
6
+ export declare const PsThemeToggle: ({ className, mode, type, ...props }: PsThemeToggleProps) => import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsThemeToggle = 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 PsThemeToggle = ({ className, mode, type = 'button', ...props }) => {
8
+ const isDarkMode = mode === ps_design_system_1.PallasoftThemeMode.Dark;
9
+ return ((0, jsx_runtime_1.jsxs)("button", { "aria-label": isDarkMode ? 'Switch to light mode' : 'Switch to dark mode', className: ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring ps-glass flex h-11 items-center gap-2 rounded-2xl px-3 text-sm font-bold text-ink transition hover:border-primary/30 hover:text-primary', className), type: type, ...props, children: [(0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { className: 'h-5 w-5', iconKey: isDarkMode ? 'sun' : 'moon' }), (0, jsx_runtime_1.jsx)("span", { className: 'hidden sm:inline', children: ps_design_system_1.PallasoftThemeModeService.getNextMode(mode) === ps_design_system_1.PallasoftThemeMode.Dark ? 'Dark' : 'Light' })] }));
10
+ };
11
+ exports.PsThemeToggle = PsThemeToggle;
@@ -0,0 +1,7 @@
1
+ export interface PsVoiceOrbProps {
2
+ className?: string;
3
+ level?: number;
4
+ size?: number;
5
+ variant?: 'default' | 'error';
6
+ }
7
+ export declare const PsVoiceOrb: (props: PsVoiceOrbProps) => import("react").JSX.Element;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsVoiceOrb = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
7
+ const PsVoiceOrb = (props) => {
8
+ const size = typeof props.size === 'number' ? props.size : 64;
9
+ const level = typeof props.level === 'number' ? clamp(props.level, 0, 1) : 0;
10
+ const isError = props.variant === 'error';
11
+ const glowGradient = isError
12
+ ? 'radial-gradient(circle, rgb(var(--ps-color-danger-rgb) / 0.55), rgb(var(--ps-color-danger-rgb) / 0.2) 70%, transparent)'
13
+ : 'radial-gradient(circle, rgb(var(--ps-color-primary-rgb) / 0.55), rgb(var(--ps-color-secondary-rgb) / 0.35) 70%, transparent)';
14
+ const coreGradient = isError
15
+ ? 'linear-gradient(135deg, rgb(var(--ps-color-danger-rgb)), rgb(var(--ps-color-danger-rgb) / 0.75))'
16
+ : 'linear-gradient(135deg, rgb(var(--ps-color-primary-rgb)), rgb(var(--ps-color-primary-dark-rgb)))';
17
+ return ((0, jsx_runtime_1.jsxs)("span", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-voice-orb relative inline-flex shrink-0 items-center justify-center', props.className), style: { width: size, height: size }, children: [(0, jsx_runtime_1.jsx)("style", { children: `
18
+ @keyframes ps-voice-orb-breathe {
19
+ 0%, 100% { transform: scale(1); }
20
+ 50% { transform: scale(1.06); }
21
+ }
22
+ @keyframes ps-voice-orb-drift {
23
+ 0%, 100% { border-radius: 46% 54% 58% 42% / 44% 46% 54% 56%; }
24
+ 50% { border-radius: 54% 46% 42% 58% / 56% 44% 56% 44%; }
25
+ }
26
+ .ps-voice-orb-glow {
27
+ animation: ps-voice-orb-drift 9s ease-in-out infinite;
28
+ }
29
+ .ps-voice-orb-core {
30
+ animation: ps-voice-orb-breathe 3.2s ease-in-out infinite;
31
+ }
32
+ ` }), (0, jsx_runtime_1.jsx)("span", { className: 'ps-voice-orb-glow absolute inset-[10%] blur-md transition-transform duration-150 ease-out', style: {
33
+ background: glowGradient,
34
+ transform: `scale(${1 + level * 0.55})`,
35
+ } }), (0, jsx_runtime_1.jsx)("span", { className: 'ps-voice-orb-core absolute inset-[26%] flex items-center justify-center rounded-full shadow-lg transition-transform duration-150 ease-out', style: {
36
+ background: coreGradient,
37
+ transform: `scale(${1 + level * 0.28})`,
38
+ }, children: (0, jsx_runtime_1.jsx)("span", { className: 'h-[28%] w-[28%] rounded-full bg-white/70' }) })] }));
39
+ };
40
+ exports.PsVoiceOrb = PsVoiceOrb;
@@ -0,0 +1,9 @@
1
+ export interface UsePsAudioSpectrumOptions {
2
+ barCount?: number;
3
+ fftSize?: number;
4
+ smoothing?: number;
5
+ }
6
+ export interface UsePsAudioSpectrum {
7
+ levels: number[];
8
+ }
9
+ export declare const usePsAudioSpectrum: (audioElement: HTMLAudioElement | null, options?: UsePsAudioSpectrumOptions) => UsePsAudioSpectrum;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePsAudioSpectrum = void 0;
4
+ const react_1 = require("react");
5
+ const DefaultBarCount = 12;
6
+ const DefaultFftSize = 128;
7
+ const DefaultSmoothing = 0.75;
8
+ const usePsAudioSpectrum = (audioElement, options = {}) => {
9
+ const barCount = typeof options.barCount === 'number' ? options.barCount : DefaultBarCount;
10
+ const fftSize = typeof options.fftSize === 'number' ? options.fftSize : DefaultFftSize;
11
+ const smoothing = typeof options.smoothing === 'number' ? options.smoothing : DefaultSmoothing;
12
+ const [levels, setLevels] = (0, react_1.useState)(() => Array(barCount).fill(0));
13
+ const configRef = (0, react_1.useRef)({ barCount, fftSize, smoothing });
14
+ configRef.current = { barCount, fftSize, smoothing };
15
+ (0, react_1.useEffect)(() => {
16
+ if (!audioElement)
17
+ return;
18
+ const AudioContextClass = typeof window === 'undefined'
19
+ ? undefined
20
+ : window.AudioContext || window.webkitAudioContext;
21
+ if (!AudioContextClass)
22
+ return;
23
+ const audioContext = new AudioContextClass();
24
+ const analyser = audioContext.createAnalyser();
25
+ analyser.fftSize = configRef.current.fftSize;
26
+ analyser.smoothingTimeConstant = configRef.current.smoothing;
27
+ const source = audioContext.createMediaElementSource(audioElement);
28
+ source.connect(analyser);
29
+ analyser.connect(audioContext.destination);
30
+ const buffer = new Uint8Array(analyser.frequencyBinCount);
31
+ let animationFrame;
32
+ const tick = () => {
33
+ analyser.getByteFrequencyData(buffer);
34
+ const activeBarCount = configRef.current.barCount;
35
+ const binsPerBar = Math.max(1, Math.floor(buffer.length / activeBarCount));
36
+ const nextLevels = [];
37
+ for (let bar = 0; bar < activeBarCount; bar++) {
38
+ let sum = 0;
39
+ const start = bar * binsPerBar;
40
+ const end = start + binsPerBar;
41
+ for (let bin = start; bin < end; bin++) {
42
+ sum += bin < buffer.length ? buffer[bin] : 0;
43
+ }
44
+ nextLevels.push(Math.min(1, sum / binsPerBar / 255));
45
+ }
46
+ setLevels(nextLevels);
47
+ animationFrame = requestAnimationFrame(tick);
48
+ };
49
+ const resumeAudioContext = () => {
50
+ if (audioContext.state === 'suspended')
51
+ audioContext.resume().catch(() => undefined);
52
+ };
53
+ resumeAudioContext();
54
+ audioElement.addEventListener('play', resumeAudioContext);
55
+ animationFrame = requestAnimationFrame(tick);
56
+ return () => {
57
+ cancelAnimationFrame(animationFrame);
58
+ audioElement.removeEventListener('play', resumeAudioContext);
59
+ source.disconnect();
60
+ analyser.disconnect();
61
+ audioContext.close().catch(() => undefined);
62
+ };
63
+ }, [audioElement]);
64
+ return { levels };
65
+ };
66
+ exports.usePsAudioSpectrum = usePsAudioSpectrum;
@@ -0,0 +1,5 @@
1
+ export interface UsePsExitTransitionResult {
2
+ isClosing: boolean;
3
+ shouldRender: boolean;
4
+ }
5
+ export declare const usePsExitTransition: (isOpen: boolean, durationMs?: number) => UsePsExitTransitionResult;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePsExitTransition = void 0;
4
+ const react_1 = require("react");
5
+ const usePsExitTransition = (isOpen, durationMs = 180) => {
6
+ const [shouldRender, setShouldRender] = (0, react_1.useState)(isOpen);
7
+ const [isClosing, setIsClosing] = (0, react_1.useState)(false);
8
+ const timeoutRef = (0, react_1.useRef)();
9
+ const hasRenderedRef = (0, react_1.useRef)(isOpen);
10
+ (0, react_1.useEffect)(() => {
11
+ if (timeoutRef.current)
12
+ clearTimeout(timeoutRef.current);
13
+ if (isOpen) {
14
+ setShouldRender(true);
15
+ setIsClosing(false);
16
+ return;
17
+ }
18
+ if (!hasRenderedRef.current)
19
+ return;
20
+ setIsClosing(true);
21
+ timeoutRef.current = setTimeout(() => {
22
+ setShouldRender(false);
23
+ setIsClosing(false);
24
+ }, durationMs);
25
+ return () => {
26
+ if (timeoutRef.current)
27
+ clearTimeout(timeoutRef.current);
28
+ };
29
+ // eslint-disable-next-line react-hooks/exhaustive-deps
30
+ }, [isOpen, durationMs]);
31
+ hasRenderedRef.current = shouldRender;
32
+ return { isClosing, shouldRender };
33
+ };
34
+ exports.usePsExitTransition = usePsExitTransition;
@@ -0,0 +1,4 @@
1
+ export interface UsePsHideOnScrollOptions {
2
+ threshold?: number;
3
+ }
4
+ export declare const usePsHideOnScroll: (options?: UsePsHideOnScrollOptions) => boolean;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePsHideOnScroll = void 0;
4
+ const react_1 = require("react");
5
+ const usePsHideOnScroll = (options = {}) => {
6
+ const { threshold = 8 } = options;
7
+ const [hidden, setHidden] = (0, react_1.useState)(false);
8
+ const lastScrollY = (0, react_1.useRef)(0);
9
+ (0, react_1.useEffect)(() => {
10
+ lastScrollY.current = window.scrollY;
11
+ const onScroll = () => {
12
+ const currentScrollY = window.scrollY;
13
+ const delta = currentScrollY - lastScrollY.current;
14
+ if (currentScrollY <= 0) {
15
+ setHidden(false);
16
+ lastScrollY.current = currentScrollY;
17
+ return;
18
+ }
19
+ if (Math.abs(delta) < threshold)
20
+ return;
21
+ setHidden(delta > 0);
22
+ lastScrollY.current = currentScrollY;
23
+ };
24
+ window.addEventListener('scroll', onScroll, { passive: true });
25
+ return () => window.removeEventListener('scroll', onScroll);
26
+ }, [threshold]);
27
+ return hidden;
28
+ };
29
+ exports.usePsHideOnScroll = usePsHideOnScroll;
@@ -0,0 +1,27 @@
1
+ declare global {
2
+ interface Window {
3
+ grecaptcha?: {
4
+ ready: (callback: () => void) => void;
5
+ render: (container: HTMLElement, parameters: {
6
+ 'sitekey': string;
7
+ 'size': 'invisible';
8
+ 'badge': PsRecaptchaBadgePosition;
9
+ 'callback': (token: string) => void;
10
+ 'expired-callback': () => void;
11
+ 'error-callback': () => void;
12
+ }) => number;
13
+ execute: (widgetId: number) => void;
14
+ reset: (widgetId: number) => void;
15
+ };
16
+ }
17
+ }
18
+ export type PsRecaptchaBadgePosition = 'bottomright' | 'bottomleft' | 'inline';
19
+ export interface UsePsInvisibleRecaptchaOptions {
20
+ siteKey: string;
21
+ badgePosition?: PsRecaptchaBadgePosition;
22
+ }
23
+ export interface UsePsInvisibleRecaptcha {
24
+ execute: () => Promise<string>;
25
+ isReady: boolean;
26
+ }
27
+ export declare const usePsInvisibleRecaptcha: ({ siteKey, badgePosition, }: UsePsInvisibleRecaptchaOptions) => UsePsInvisibleRecaptcha;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePsInvisibleRecaptcha = void 0;
4
+ const react_1 = require("react");
5
+ const SCRIPT_SRC = 'https://www.google.com/recaptcha/api.js?render=explicit';
6
+ let scriptLoadPromise = null;
7
+ const loadRecaptchaScript = () => {
8
+ if (scriptLoadPromise)
9
+ return scriptLoadPromise;
10
+ scriptLoadPromise = new Promise((resolve, reject) => {
11
+ const existing = document.querySelector(`script[src="${SCRIPT_SRC}"]`);
12
+ if (existing) {
13
+ existing.addEventListener('load', () => resolve());
14
+ existing.addEventListener('error', () => reject(new Error('Failed to load reCAPTCHA')));
15
+ if (window.grecaptcha)
16
+ resolve();
17
+ return;
18
+ }
19
+ const script = document.createElement('script');
20
+ script.src = SCRIPT_SRC;
21
+ script.async = true;
22
+ script.defer = true;
23
+ script.onload = () => resolve();
24
+ script.onerror = () => reject(new Error('Failed to load reCAPTCHA'));
25
+ document.head.appendChild(script);
26
+ });
27
+ return scriptLoadPromise;
28
+ };
29
+ const usePsInvisibleRecaptcha = ({ siteKey, badgePosition = 'bottomright', }) => {
30
+ const [isReady, setIsReady] = (0, react_1.useState)(false);
31
+ const widgetIdRef = (0, react_1.useRef)(null);
32
+ const containerRef = (0, react_1.useRef)(null);
33
+ const pendingRef = (0, react_1.useRef)(null);
34
+ (0, react_1.useEffect)(() => {
35
+ let isMounted = true;
36
+ const container = document.createElement('div');
37
+ container.style.display = 'none';
38
+ document.body.appendChild(container);
39
+ containerRef.current = container;
40
+ loadRecaptchaScript()
41
+ .then(() => {
42
+ if (!isMounted || !window.grecaptcha)
43
+ return;
44
+ window.grecaptcha.ready(() => {
45
+ if (!isMounted || !window.grecaptcha || !containerRef.current)
46
+ return;
47
+ widgetIdRef.current = window.grecaptcha.render(containerRef.current, {
48
+ 'sitekey': siteKey,
49
+ 'size': 'invisible',
50
+ 'badge': badgePosition,
51
+ 'callback': (token) => {
52
+ pendingRef.current?.resolve(token);
53
+ pendingRef.current = null;
54
+ },
55
+ 'expired-callback': () => {
56
+ pendingRef.current?.reject(new Error('reCAPTCHA expired'));
57
+ pendingRef.current = null;
58
+ },
59
+ 'error-callback': () => {
60
+ pendingRef.current?.reject(new Error('reCAPTCHA verification failed'));
61
+ pendingRef.current = null;
62
+ },
63
+ });
64
+ setIsReady(true);
65
+ });
66
+ })
67
+ .catch(() => {
68
+ if (isMounted)
69
+ setIsReady(false);
70
+ });
71
+ return () => {
72
+ isMounted = false;
73
+ container.remove();
74
+ containerRef.current = null;
75
+ };
76
+ }, [siteKey, badgePosition]);
77
+ const execute = (0, react_1.useCallback)(() => {
78
+ return new Promise((resolve, reject) => {
79
+ if (!window.grecaptcha || widgetIdRef.current === null) {
80
+ reject(new Error('reCAPTCHA is not ready yet'));
81
+ return;
82
+ }
83
+ pendingRef.current = { resolve, reject };
84
+ window.grecaptcha.execute(widgetIdRef.current);
85
+ }).finally(() => {
86
+ if (window.grecaptcha && widgetIdRef.current !== null) {
87
+ window.grecaptcha.reset(widgetIdRef.current);
88
+ }
89
+ });
90
+ }, []);
91
+ return { execute, isReady };
92
+ };
93
+ exports.usePsInvisibleRecaptcha = usePsInvisibleRecaptcha;
@@ -0,0 +1 @@
1
+ export declare const createPsRouteLoaderDataHook: <T>(routeId: string) => () => T;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createPsRouteLoaderDataHook = void 0;
4
+ const react_1 = require("@remix-run/react");
5
+ const createPsRouteLoaderDataHook = (routeId) => {
6
+ let cache;
7
+ return () => {
8
+ const data = (0, react_1.useRouteLoaderData)(routeId);
9
+ if (data !== undefined)
10
+ cache = data;
11
+ if (cache === undefined)
12
+ throw new Error(`No loader data found for route "${routeId}"`);
13
+ return cache;
14
+ };
15
+ };
16
+ exports.createPsRouteLoaderDataHook = createPsRouteLoaderDataHook;
@@ -0,0 +1,2 @@
1
+ import { PallasoftViewportTier } from '@pallasoft/ps-design-system';
2
+ export declare const usePsViewport: (defaultTier?: PallasoftViewportTier) => PallasoftViewportTier;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePsViewport = void 0;
4
+ const react_1 = require("react");
5
+ const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const usePsViewport = (defaultTier = ps_design_system_1.PallasoftViewportTier.Desktop) => {
7
+ const [tier, setTier] = (0, react_1.useState)(defaultTier);
8
+ (0, react_1.useEffect)(() => {
9
+ const resolve = () => setTier(ps_design_system_1.PallasoftViewportService.getTier(window.innerWidth));
10
+ resolve();
11
+ const tabletQuery = window.matchMedia(ps_design_system_1.PallasoftViewportService.getTabletQuery());
12
+ const desktopQuery = window.matchMedia(ps_design_system_1.PallasoftViewportService.getDesktopQuery());
13
+ tabletQuery.addEventListener('change', resolve);
14
+ desktopQuery.addEventListener('change', resolve);
15
+ return () => {
16
+ tabletQuery.removeEventListener('change', resolve);
17
+ desktopQuery.removeEventListener('change', resolve);
18
+ };
19
+ }, []);
20
+ return tier;
21
+ };
22
+ exports.usePsViewport = usePsViewport;
@@ -0,0 +1,26 @@
1
+ export * from './components/ps-accordion';
2
+ export * from './components/ps-action-tile';
3
+ export * from './components/ps-audio-spectrum';
4
+ export * from './components/ps-badge';
5
+ export * from './components/ps-button';
6
+ export * from './components/ps-card';
7
+ export * from './components/ps-draggable-grid';
8
+ export * from './components/ps-empty-state';
9
+ export * from './components/ps-fab';
10
+ export * from './components/ps-infinite-scroll';
11
+ export * from './components/ps-list-tile';
12
+ export * from './components/ps-metric-card';
13
+ export * from './components/ps-pill';
14
+ export * from './components/ps-reflective-app-shell';
15
+ export * from './components/ps-section-header';
16
+ export * from './components/ps-skeleton';
17
+ export * from './components/ps-spinner';
18
+ export * from './components/ps-theme-toggle';
19
+ export * from './components/ps-voice-orb';
20
+ export * from './hooks/use-ps-audio-spectrum';
21
+ export * from './hooks/use-ps-exit-transition';
22
+ export * from './hooks/use-ps-hide-on-scroll';
23
+ export * from './hooks/use-ps-invisible-recaptcha';
24
+ export * from './hooks/use-ps-route-loader-data';
25
+ export * from './hooks/use-ps-viewport';
26
+ export * from './interfaces/ps-component-props';
package/dist/index.js ADDED
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./components/ps-accordion"), exports);
18
+ __exportStar(require("./components/ps-action-tile"), exports);
19
+ __exportStar(require("./components/ps-audio-spectrum"), exports);
20
+ __exportStar(require("./components/ps-badge"), exports);
21
+ __exportStar(require("./components/ps-button"), exports);
22
+ __exportStar(require("./components/ps-card"), exports);
23
+ __exportStar(require("./components/ps-draggable-grid"), exports);
24
+ __exportStar(require("./components/ps-empty-state"), exports);
25
+ __exportStar(require("./components/ps-fab"), exports);
26
+ __exportStar(require("./components/ps-infinite-scroll"), exports);
27
+ __exportStar(require("./components/ps-list-tile"), exports);
28
+ __exportStar(require("./components/ps-metric-card"), exports);
29
+ __exportStar(require("./components/ps-pill"), exports);
30
+ __exportStar(require("./components/ps-reflective-app-shell"), exports);
31
+ __exportStar(require("./components/ps-section-header"), exports);
32
+ __exportStar(require("./components/ps-skeleton"), exports);
33
+ __exportStar(require("./components/ps-spinner"), exports);
34
+ __exportStar(require("./components/ps-theme-toggle"), exports);
35
+ __exportStar(require("./components/ps-voice-orb"), exports);
36
+ __exportStar(require("./hooks/use-ps-audio-spectrum"), exports);
37
+ __exportStar(require("./hooks/use-ps-exit-transition"), exports);
38
+ __exportStar(require("./hooks/use-ps-hide-on-scroll"), exports);
39
+ __exportStar(require("./hooks/use-ps-invisible-recaptcha"), exports);
40
+ __exportStar(require("./hooks/use-ps-route-loader-data"), exports);
41
+ __exportStar(require("./hooks/use-ps-viewport"), exports);
42
+ __exportStar(require("./interfaces/ps-component-props"), exports);
@@ -0,0 +1,7 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface PsBaseComponentProps {
3
+ className?: string;
4
+ }
5
+ export interface PsChildrenComponentProps extends PsBaseComponentProps {
6
+ children: ReactNode;
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@pallasoft/ps-core-components",
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "require": "./dist/index.js",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "/dist"
16
+ ],
17
+ "scripts": {
18
+ "husky:prepare": "npx husky",
19
+ "build": "tsc",
20
+ "setup:graphify": "sh scripts/graphify-setup.sh",
21
+ "update:graphify": "sh scripts/graphify-update.sh",
22
+ "lint": "npm run lint:eslint",
23
+ "lint:fix": "npm run lint:eslint:fix",
24
+ "format:check": "npx prettier --check --ignore-unknown \"src/**/*.{ts,tsx,json,md}\"",
25
+ "format:prettier": "npx prettier --write \"src/**/*.{ts,tsx,json,md}\"",
26
+ "lint:eslint": "npx eslint \"src/**/*.{ts,tsx}\"",
27
+ "lint:eslint:fix": "npx eslint --fix \"src/**/*.{ts,tsx}\"",
28
+ "release": "semantic-release"
29
+ },
30
+ "dependencies": {
31
+ "@pallasoft/ps-design-system": "^1.0.0",
32
+ "@pallasoft/ps-icons": "^1.0.0",
33
+ "@radix-ui/react-accordion": "^1.2.12",
34
+ "react-infinite-scroll-component": "^6.1.1"
35
+ },
36
+ "peerDependencies": {
37
+ "@remix-run/react": "^2.0.0",
38
+ "react": "^18 || ^19"
39
+ },
40
+ "devDependencies": {
41
+ "@commitlint/cli": "^20.1.0",
42
+ "@commitlint/config-conventional": "^20.0.0",
43
+ "@eslint/compat": "^2.0.0",
44
+ "@ianvs/prettier-plugin-sort-imports": "^4.7.0",
45
+ "@semantic-release/changelog": "^6.0.3",
46
+ "@semantic-release/commit-analyzer": "^13.0.0",
47
+ "@semantic-release/git": "^10.0.1",
48
+ "@semantic-release/github": "^11.0.1",
49
+ "@semantic-release/npm": "^12.0.0",
50
+ "@semantic-release/release-notes-generator": "^14.0.1",
51
+ "@types/react": "^18.3.28",
52
+ "@typescript-eslint/eslint-plugin": "^8.48.0",
53
+ "@typescript-eslint/parser": "^8.48.0",
54
+ "commitlint": "^20.1.0",
55
+ "conventional-changelog-conventionalcommits": "^7.0.2",
56
+ "eslint": "^9.39.1",
57
+ "eslint-config-prettier": "^10.1.8",
58
+ "eslint-plugin-import": "^2.32.0",
59
+ "eslint-plugin-prettier": "^5.5.4",
60
+ "husky": "^9.1.7",
61
+ "lint-staged": "^16.2.7",
62
+ "prettier": "^3.6.2",
63
+ "semantic-release": "^24.2.0",
64
+ "ts-node": "^10.9.2",
65
+ "typescript": "^5.9.3"
66
+ },
67
+ "publishConfig": {
68
+ "access": "public",
69
+ "registry": "https://registry.npmjs.org/"
70
+ },
71
+ "repository": {
72
+ "type": "git",
73
+ "url": "git+https://github.com/pallasoft/ps-core-components.git"
74
+ }
75
+ }