@paroicms/react-ui 0.4.3 → 0.5.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.
- package/dist/Accordion.d.ts +10 -0
- package/dist/{PuAccordion.jsx → Accordion.js} +5 -8
- package/dist/Alert.d.ts +10 -0
- package/dist/Alert.js +7 -0
- package/dist/Badge.d.ts +8 -0
- package/dist/Badge.js +6 -0
- package/dist/Breadcrumb.d.ts +14 -0
- package/dist/Breadcrumb.js +10 -0
- package/dist/Button.d.ts +36 -0
- package/dist/Button.js +74 -0
- package/dist/Card.d.ts +10 -0
- package/dist/Card.js +7 -0
- package/dist/Checkbox.d.ts +9 -0
- package/dist/Checkbox.js +12 -0
- package/dist/Chip.d.ts +8 -0
- package/dist/Chip.js +7 -0
- package/dist/Column.d.ts +14 -0
- package/dist/Column.js +7 -0
- package/dist/DataTable.d.ts +30 -0
- package/dist/DataTable.js +26 -0
- package/dist/DateInput.d.ts +9 -0
- package/dist/DateInput.js +7 -0
- package/dist/Dialog.d.ts +13 -0
- package/dist/Dialog.js +55 -0
- package/dist/Inplace.d.ts +12 -0
- package/dist/Inplace.js +16 -0
- package/dist/InputNumber.d.ts +10 -0
- package/dist/InputNumber.js +19 -0
- package/dist/InputText.d.ts +14 -0
- package/dist/InputText.js +11 -0
- package/dist/MenuItem.d.ts +8 -0
- package/dist/MenuItem.js +18 -0
- package/dist/MultiSelect.d.ts +17 -0
- package/dist/MultiSelect.js +50 -0
- package/dist/Panel.d.ts +11 -0
- package/dist/Panel.js +9 -0
- package/dist/PasswordInput.d.ts +8 -0
- package/dist/PasswordInput.js +10 -0
- package/dist/PopupMenu.d.ts +20 -0
- package/dist/{PuPopupMenu.jsx → PopupMenu.js} +45 -36
- package/dist/RadioButton.d.ts +9 -0
- package/dist/RadioButton.js +12 -0
- package/dist/Select.d.ts +18 -0
- package/dist/Select.js +11 -0
- package/dist/SideMenu.d.ts +5 -0
- package/dist/SideMenu.js +13 -0
- package/dist/SortableList.d.ts +19 -0
- package/dist/SortableList.js +27 -0
- package/dist/Spinner.d.ts +2 -0
- package/dist/Spinner.js +5 -0
- package/dist/SplitButton.d.ts +24 -0
- package/dist/SplitButton.js +42 -0
- package/dist/Switch.d.ts +9 -0
- package/dist/Switch.js +12 -0
- package/dist/Tabs.d.ts +22 -0
- package/dist/Tabs.js +21 -0
- package/dist/Textarea.d.ts +8 -0
- package/dist/Textarea.js +7 -0
- package/dist/ToggleButton.d.ts +11 -0
- package/dist/ToggleButton.js +6 -0
- package/dist/ToggleGroup.d.ts +15 -0
- package/dist/ToggleGroup.js +6 -0
- package/dist/Tooltip.d.ts +10 -0
- package/dist/Tooltip.js +50 -0
- package/dist/Tree.d.ts +22 -0
- package/dist/Tree.js +43 -0
- package/dist/alert-stack.d.ts +18 -0
- package/dist/alert-stack.js +72 -0
- package/dist/index.d.ts +36 -10
- package/dist/index.js +45 -10
- package/dist/paroi-ui-lib-types.d.ts +4 -4
- package/dist/react-ui-provider.d.ts +15 -0
- package/dist/react-ui-provider.js +22 -0
- package/package.json +18 -4
- package/styles/Accordion.css +46 -0
- package/styles/Alert.css +76 -0
- package/styles/Badge.css +59 -0
- package/styles/Breadcrumb.css +57 -0
- package/styles/Button.css +167 -0
- package/styles/Card.css +28 -0
- package/styles/Checkbox.css +61 -0
- package/styles/Chip.css +35 -0
- package/styles/DataTable.css +176 -0
- package/styles/DateInput.css +59 -0
- package/styles/Dialog.css +77 -0
- package/styles/Inplace.css +44 -0
- package/styles/InputNumber.css +60 -0
- package/styles/InputText.css +99 -0
- package/styles/MenuItem.css +169 -0
- package/styles/MultiSelect.css +158 -0
- package/styles/Panel.css +40 -0
- package/styles/PasswordInput.css +80 -0
- package/styles/PopupMenu.css +37 -0
- package/styles/RadioButton.css +60 -0
- package/styles/Select.css +72 -0
- package/styles/SideMenu.css +7 -0
- package/styles/SortableList.css +32 -0
- package/styles/Spinner.css +30 -0
- package/styles/SplitButton.css +143 -0
- package/styles/Switch.css +60 -0
- package/styles/Tabs.css +94 -0
- package/styles/Textarea.css +66 -0
- package/styles/ToggleButton.css +36 -0
- package/styles/ToggleGroup.css +55 -0
- package/styles/Tooltip.css +34 -0
- package/styles/Tree.css +161 -0
- package/styles/theme/base.css +40 -0
- package/styles/theme/common.css +410 -0
- package/styles/theme/index.css +15 -0
- package/styles/theme/margins.css +119 -0
- package/styles/theme/reset.css +119 -0
- package/styles/theme/tokens.css +226 -0
- package/dist/PuAccordion.d.ts +0 -9
- package/dist/PuButton.d.ts +0 -14
- package/dist/PuButton.jsx +0 -15
- package/dist/PuCheckbox.d.ts +0 -8
- package/dist/PuCheckbox.jsx +0 -13
- package/dist/PuInput.d.ts +0 -10
- package/dist/PuInput.jsx +0 -13
- package/dist/PuMenuItem.d.ts +0 -7
- package/dist/PuMenuItem.jsx +0 -33
- package/dist/PuPopupMenu.d.ts +0 -14
- package/dist/PuSelect.d.ts +0 -17
- package/dist/PuSelect.jsx +0 -24
- package/dist/PuSideMenu.d.ts +0 -4
- package/dist/PuSideMenu.jsx +0 -15
- package/dist/PuSpinner.d.ts +0 -1
- package/dist/PuSpinner.jsx +0 -3
- package/dist/svg-icons.d.ts +0 -5
- package/dist/svg-icons.jsx +0 -30
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import "../styles/MultiSelect.css";
|
|
3
|
+
import { clsx } from "clsx";
|
|
4
|
+
import { ChevronDown, X } from "lucide-react";
|
|
5
|
+
import { useEffect, useRef, useState } from "react";
|
|
6
|
+
export function MultiSelect({ value, onChange, options, label, error, className, placeholder, disabled, }) {
|
|
7
|
+
const [open, setOpen] = useState(false);
|
|
8
|
+
const containerRef = useRef(null);
|
|
9
|
+
// Close dropdown when clicking outside
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (!open)
|
|
12
|
+
return;
|
|
13
|
+
const handleClickOutside = (e) => {
|
|
14
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
15
|
+
setOpen(false);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
19
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
20
|
+
}, [open]);
|
|
21
|
+
const toggleOption = (optionValue) => {
|
|
22
|
+
if (value.includes(optionValue)) {
|
|
23
|
+
onChange(value.filter((v) => v !== optionValue));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
onChange([...value, optionValue]);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const removeValue = (optionValue) => {
|
|
30
|
+
onChange(value.filter((v) => v !== optionValue));
|
|
31
|
+
};
|
|
32
|
+
const selectedOptions = options.filter((opt) => value.includes(opt.value));
|
|
33
|
+
const controlElement = (_jsxs(_Fragment, { children: [_jsxs("span", { className: clsx("PaMultiSelect-control", open && "open"), onClick: () => !disabled && setOpen(!open), onKeyDown: (e) => {
|
|
34
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
35
|
+
e.preventDefault();
|
|
36
|
+
if (!disabled)
|
|
37
|
+
setOpen(!open);
|
|
38
|
+
}
|
|
39
|
+
}, tabIndex: disabled ? -1 : 0, role: "combobox", "aria-expanded": open, "aria-haspopup": "listbox", children: [_jsx("span", { className: "PaMultiSelect-values", children: selectedOptions.length > 0 ? (selectedOptions.map((opt) => (_jsxs("span", { className: "PaMultiSelect-chip", children: [opt.label, _jsx("button", { type: "button", className: "PaMultiSelect-chipRemove", onClick: (e) => {
|
|
40
|
+
e.stopPropagation();
|
|
41
|
+
removeValue(opt.value);
|
|
42
|
+
}, "aria-label": `Remove ${opt.label}`, children: _jsx(X, { size: 12 }) })] }, opt.value)))) : (_jsx("span", { className: "PaMultiSelect-placeholder", children: placeholder })) }), _jsx(ChevronDown, { className: "PaMultiSelect-icon", size: 16 })] }), open && (_jsx("span", { className: "PaMultiSelect-dropdown", role: "listbox", children: options.map((option) => (_jsxs("span", { className: clsx("PaMultiSelect-option", value.includes(option.value) && "selected", option.disabled && "disabled"), onClick: () => !option.disabled && toggleOption(option.value), onKeyDown: (e) => {
|
|
43
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
if (!option.disabled)
|
|
46
|
+
toggleOption(option.value);
|
|
47
|
+
}
|
|
48
|
+
}, tabIndex: option.disabled ? -1 : 0, role: "option", "aria-selected": value.includes(option.value), children: [_jsx("span", { className: "PaMultiSelect-checkbox", children: value.includes(option.value) && "✓" }), option.label] }, option.value))) }))] }));
|
|
49
|
+
return (_jsxs("span", { ref: containerRef, className: clsx("PaMultiSelect", error && "error", disabled && "disabled", className), children: [label ? (_jsxs("label", { className: "PaMultiSelect-wrapper", children: [_jsx("span", { className: "PaMultiSelect-label", children: label }), controlElement] })) : (controlElement), error && _jsx("span", { className: "PaMultiSelect-error", children: error })] }));
|
|
50
|
+
}
|
package/dist/Panel.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import "../styles/Panel.css";
|
|
3
|
+
export interface PanelProps {
|
|
4
|
+
header: ReactNode;
|
|
5
|
+
collapsed?: boolean;
|
|
6
|
+
onToggle?: () => void;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
className?: string;
|
|
9
|
+
collapsible?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function Panel({ header, collapsed, onToggle, children, className, collapsible, }: PanelProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/Panel.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { clsx } from "clsx";
|
|
3
|
+
import { ChevronDown, ChevronRight } from "lucide-react";
|
|
4
|
+
import "../styles/Panel.css";
|
|
5
|
+
export function Panel({ header, collapsed = false, onToggle, children, className, collapsible = true, }) {
|
|
6
|
+
const isCollapsible = collapsible && onToggle;
|
|
7
|
+
const headerContent = (_jsxs(_Fragment, { children: [isCollapsible && (_jsx("span", { className: "PaPanel-chevron", children: collapsed ? _jsx(ChevronRight, { size: 16 }) : _jsx(ChevronDown, { size: 16 }) })), _jsx("span", { className: "PaPanel-title", children: header })] }));
|
|
8
|
+
return (_jsxs("div", { className: clsx("PaPanel", collapsed && "collapsed", className), children: [isCollapsible ? (_jsx("button", { type: "button", className: clsx("PaPanel-header", "collapsible"), onClick: onToggle, "aria-expanded": !collapsed, children: headerContent })) : (_jsx("div", { className: "PaPanel-header", children: headerContent })), !collapsed && _jsx("div", { className: "PaPanel-content", children: children })] }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "../styles/PasswordInput.css";
|
|
2
|
+
import { type InputHTMLAttributes, type Ref } from "react";
|
|
3
|
+
export interface PasswordInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type"> {
|
|
4
|
+
label?: string;
|
|
5
|
+
error?: string;
|
|
6
|
+
ref?: Ref<HTMLInputElement>;
|
|
7
|
+
}
|
|
8
|
+
export declare function PasswordInput({ className, label, error, ref, ...props }: PasswordInputProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "../styles/PasswordInput.css";
|
|
3
|
+
import { clsx } from "clsx";
|
|
4
|
+
import { Eye, EyeOff } from "lucide-react";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
export function PasswordInput({ className, label, error, ref, ...props }) {
|
|
7
|
+
const [visible, setVisible] = useState(false);
|
|
8
|
+
const inputWithToggle = (_jsxs("span", { className: clsx("PaPasswordInput-inputWrapper", error && "error"), children: [_jsx("input", { ref: ref, type: visible ? "text" : "password", className: "PaPasswordInput-input", ...props }), _jsx("button", { type: "button", className: "PaPasswordInput-toggle", onClick: () => setVisible(!visible), tabIndex: -1, "aria-label": visible ? "Hide password" : "Show password", children: visible ? _jsx(EyeOff, { size: 16 }) : _jsx(Eye, { size: 16 }) })] }));
|
|
9
|
+
return (_jsxs("span", { className: clsx("PaPasswordInput", className), children: [label ? (_jsxs("label", { className: "PaPasswordInput-wrapper", children: [_jsx("span", { className: "PaPasswordInput-label", children: label }), inputWithToggle] })) : (inputWithToggle), error && _jsx("span", { className: "PaPasswordInput-error", children: error })] }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import "../styles/PopupMenu.css";
|
|
2
|
+
import { type MouseEvent, type ReactNode, type Ref } from "react";
|
|
3
|
+
import type { MenuNode } from "./paroi-ui-lib-types.js";
|
|
4
|
+
export interface PopupMenuProps {
|
|
5
|
+
items: MenuNode[];
|
|
6
|
+
className?: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
autoPosition?: boolean;
|
|
9
|
+
/** Position of the popup relative to the trigger: 'bottom' (default) or 'top' */
|
|
10
|
+
position?: "bottom" | "top";
|
|
11
|
+
/** Optional custom trigger element. If provided, it will be used instead of the default button. */
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
ref?: Ref<PopupMenuRef>;
|
|
14
|
+
}
|
|
15
|
+
export interface PopupMenuRef {
|
|
16
|
+
toggle: (event: MouseEvent) => void;
|
|
17
|
+
show: (event: MouseEvent) => void;
|
|
18
|
+
hide: () => void;
|
|
19
|
+
}
|
|
20
|
+
export declare function PopupMenu({ items, className, id, autoPosition, position, children, ref, }: PopupMenuProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "../styles/PopupMenu.css";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { ChevronRight, ChevronsDown } from "lucide-react";
|
|
5
|
+
import { cloneElement, isValidElement, useEffect, useImperativeHandle, useRef, } from "react";
|
|
6
|
+
import { Button } from "./Button.js";
|
|
7
|
+
import { MenuItem } from "./MenuItem.js";
|
|
5
8
|
const isPopoverSupported = typeof HTMLElement !== "undefined" && "showPopover" in HTMLElement.prototype;
|
|
6
9
|
if (!isPopoverSupported) {
|
|
7
|
-
console?.error("
|
|
10
|
+
console?.error("PopupMenu: Popover API is not supported in this browser.");
|
|
8
11
|
}
|
|
9
12
|
let seq = 0;
|
|
10
13
|
/**
|
|
@@ -23,12 +26,12 @@ function setupPopoverPositioning(element, getPosition) {
|
|
|
23
26
|
element.addEventListener("toggle", handleToggle);
|
|
24
27
|
return () => element.removeEventListener("toggle", handleToggle);
|
|
25
28
|
}
|
|
26
|
-
export
|
|
29
|
+
export function PopupMenu({ items, className, id, autoPosition = true, position = "bottom", children, ref, }) {
|
|
27
30
|
const menuRef = useRef(null);
|
|
28
|
-
const
|
|
31
|
+
const triggerRef = useRef(null);
|
|
29
32
|
const menuIdRef = useRef(undefined);
|
|
30
33
|
if (menuIdRef.current === undefined) {
|
|
31
|
-
menuIdRef.current = id ?? `
|
|
34
|
+
menuIdRef.current = id ?? `pa-popup-menu-${++seq}`;
|
|
32
35
|
}
|
|
33
36
|
const toggle = (_ev) => {
|
|
34
37
|
if (menuRef.current) {
|
|
@@ -52,43 +55,55 @@ export const PuPopupMenu = forwardRef(function PuPopupMenu({ items, className =
|
|
|
52
55
|
}));
|
|
53
56
|
// Position popover when it opens
|
|
54
57
|
useEffect(() => {
|
|
55
|
-
if (!autoPosition || !menuRef.current || !
|
|
58
|
+
if (!autoPosition || !menuRef.current || !triggerRef.current)
|
|
56
59
|
return;
|
|
57
60
|
const menuEl = menuRef.current;
|
|
58
61
|
return setupPopoverPositioning(menuEl, () => {
|
|
59
|
-
const rect =
|
|
62
|
+
const rect = triggerRef.current?.getBoundingClientRect();
|
|
63
|
+
if (!rect)
|
|
64
|
+
return { top: 0, left: 0 };
|
|
65
|
+
if (position === "top") {
|
|
66
|
+
// Position above the trigger
|
|
67
|
+
const menuHeight = menuEl.offsetHeight || 150; // Fallback height
|
|
68
|
+
return {
|
|
69
|
+
top: rect.top + window.scrollY - menuHeight - 8,
|
|
70
|
+
left: rect.left + window.scrollX,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Default: position below
|
|
60
74
|
return {
|
|
61
|
-
top: rect
|
|
62
|
-
left: rect
|
|
75
|
+
top: rect.bottom + window.scrollY,
|
|
76
|
+
left: rect.left + window.scrollX,
|
|
63
77
|
};
|
|
64
78
|
});
|
|
65
|
-
}, [autoPosition]);
|
|
66
|
-
const
|
|
79
|
+
}, [autoPosition, position]);
|
|
80
|
+
const triggerProps = {
|
|
67
81
|
popoverTarget: menuIdRef.current,
|
|
68
82
|
popoverTargetAction: "toggle",
|
|
69
83
|
};
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
});
|
|
84
|
+
// Render custom trigger if provided, otherwise use default button
|
|
85
|
+
const renderTrigger = () => {
|
|
86
|
+
if (children && isValidElement(children)) {
|
|
87
|
+
return cloneElement(children, {
|
|
88
|
+
ref: triggerRef,
|
|
89
|
+
"aria-controls": menuIdRef.current,
|
|
90
|
+
"aria-haspopup": true,
|
|
91
|
+
...triggerProps,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return (_jsx(Button, { ref: triggerRef, className: clsx(className, "borderless"), outlined: true, severity: "secondary", "aria-label": "unfold", "aria-controls": menuIdRef.current, "aria-haspopup": true, ...triggerProps, children: _jsx(ChevronsDown, { size: 16 }) }));
|
|
95
|
+
};
|
|
96
|
+
return (_jsxs(_Fragment, { children: [renderTrigger(), _jsx("div", { className: "PaPopupMenu", ref: menuRef, id: menuIdRef.current, popover: "auto", children: _jsx("div", { className: "PaPopupMenu-content", children: items.map((item) => (_jsx(PopupMenuItemWrapper, { item: item, onHide: hide }, item.key))) }) })] }));
|
|
97
|
+
}
|
|
83
98
|
function PopupMenuItemWrapper({ item, onHide }) {
|
|
84
99
|
const subMenuRef = useRef(null);
|
|
85
|
-
const subMenuId = useRef(`
|
|
100
|
+
const subMenuId = useRef(`pa-submenu-${item.key}-${Math.random().toString(36).substring(2, 9)}`);
|
|
86
101
|
// For items with submenu, override the command to toggle the submenu
|
|
87
102
|
const itemWithSubmenuHandling = { ...item };
|
|
88
103
|
if (item.subMenu && item.subMenu.length > 0) {
|
|
89
104
|
// Create submenu trigger icon
|
|
90
105
|
const originalIcon = item.icon;
|
|
91
|
-
itemWithSubmenuHandling.icon = originalIcon ||
|
|
106
|
+
itemWithSubmenuHandling.icon = originalIcon || _jsx(ChevronRight, { size: 14 });
|
|
92
107
|
// Create a command that opens the submenu
|
|
93
108
|
const originalCommand = item.command;
|
|
94
109
|
itemWithSubmenuHandling.command = () => {
|
|
@@ -125,11 +140,5 @@ function PopupMenuItemWrapper({ item, onHide }) {
|
|
|
125
140
|
return { top: 0, left: 0 };
|
|
126
141
|
});
|
|
127
142
|
}, [item.subMenu]);
|
|
128
|
-
return (
|
|
129
|
-
<PuMenuItem item={itemWithSubmenuHandling}/>
|
|
130
|
-
|
|
131
|
-
{item.subMenu && (<div className="PuPopupMenu-submenu" popover="auto" id={subMenuId.current} ref={subMenuRef}>
|
|
132
|
-
{item.subMenu.map((subItem) => (<PopupMenuItemWrapper key={subItem.key} item={subItem} onHide={onHide}/>))}
|
|
133
|
-
</div>)}
|
|
134
|
-
</div>);
|
|
143
|
+
return (_jsxs("div", { className: "PaPopupMenu-item", popoverTargetAction: "toggle", popoverTarget: subMenuId.current, children: [_jsx(MenuItem, { item: itemWithSubmenuHandling }), item.subMenu && (_jsx("div", { className: "PaPopupMenu-submenu", popover: "auto", id: subMenuId.current, ref: subMenuRef, children: item.subMenu.map((subItem) => (_jsx(PopupMenuItemWrapper, { item: subItem, onHide: onHide }, subItem.key))) }))] }));
|
|
135
144
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { InputHTMLAttributes } from "react";
|
|
2
|
+
import "../styles/RadioButton.css";
|
|
3
|
+
export interface RadioButtonProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "onChange"> {
|
|
4
|
+
checked: boolean;
|
|
5
|
+
onChange: (checked: boolean) => void;
|
|
6
|
+
label?: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function RadioButton({ checked, onChange, label, className, ...rest }: RadioButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { clsx } from "clsx";
|
|
3
|
+
import "../styles/RadioButton.css";
|
|
4
|
+
export function RadioButton({ checked, onChange, label, className, ...rest }) {
|
|
5
|
+
const handleChange = (e) => {
|
|
6
|
+
onChange(e.target.checked);
|
|
7
|
+
};
|
|
8
|
+
if (label) {
|
|
9
|
+
return (_jsxs("label", { className: clsx("PaRadioButton", className), children: [_jsx("input", { className: "PaRadioButton-input", type: "radio", checked: checked, onChange: handleChange, ...rest }), _jsx("span", { className: "PaRadioButton-label", children: label })] }));
|
|
10
|
+
}
|
|
11
|
+
return (_jsx("input", { className: clsx("PaRadioButton-input", className), type: "radio", checked: checked, onChange: handleChange, ...rest }));
|
|
12
|
+
}
|
package/dist/Select.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import "../styles/Select.css";
|
|
2
|
+
import type { SelectHTMLAttributes } from "react";
|
|
3
|
+
export interface SelectOption {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, "onChange"> {
|
|
9
|
+
value: string;
|
|
10
|
+
onChange: (value: string) => void;
|
|
11
|
+
options: SelectOption[];
|
|
12
|
+
label?: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
className?: string;
|
|
15
|
+
fullWidth?: boolean;
|
|
16
|
+
placeholder?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function Select({ value, onChange, options, label, error, className, fullWidth, placeholder, ...rest }: SelectProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/Select.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "../styles/Select.css";
|
|
3
|
+
import { clsx } from "clsx";
|
|
4
|
+
import { ChevronDown } from "lucide-react";
|
|
5
|
+
export function Select({ value, onChange, options, label, error, className, fullWidth = false, placeholder, ...rest }) {
|
|
6
|
+
const handleChange = (e) => {
|
|
7
|
+
onChange(e.target.value);
|
|
8
|
+
};
|
|
9
|
+
const selectWithIcon = (_jsxs("span", { className: "PaSelect-fieldWrapper", children: [_jsxs("select", { className: "PaSelect-field", value: value, onChange: handleChange, ...rest, children: [placeholder && (_jsx("option", { value: "", disabled: true, children: placeholder })), options.map((option) => (_jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)))] }), _jsx(ChevronDown, { className: "PaSelect-icon", size: 16 })] }));
|
|
10
|
+
return (_jsxs("span", { className: clsx("PaSelect", error && "error", fullWidth && "fullWidth", className), children: [label ? (_jsxs("label", { className: "PaSelect-wrapper", children: [_jsx("span", { className: "PaSelect-label", children: label }), selectWithIcon] })) : (selectWithIcon), error && _jsx("span", { className: "PaSelect-error", children: error })] }));
|
|
11
|
+
}
|
package/dist/SideMenu.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import "../styles/SideMenu.css";
|
|
3
|
+
import { Accordion } from "./Accordion.js";
|
|
4
|
+
import { MenuItem } from "./MenuItem.js";
|
|
5
|
+
export function SideMenu({ menu }) {
|
|
6
|
+
return (_jsx(_Fragment, { children: menu.map((child) => (_jsx(AccordionOrMenuItem, { item: child }, child.key))) }));
|
|
7
|
+
}
|
|
8
|
+
function AccordionOrMenuItem({ item }) {
|
|
9
|
+
if (item.subMenu) {
|
|
10
|
+
return (_jsx(Accordion, { header: item, expanded: item.expanded, children: item.subMenu.map((child) => (_jsx(AccordionOrMenuItem, { item: child }, child.key))) }));
|
|
11
|
+
}
|
|
12
|
+
return _jsx(MenuItem, { item: item });
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import "../styles/SortableList.css";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
export interface SortableListProps<T> {
|
|
4
|
+
/** Items to display and reorder */
|
|
5
|
+
items: T[];
|
|
6
|
+
/** Callback when items are reordered */
|
|
7
|
+
onReorder: (items: T[]) => void;
|
|
8
|
+
/** Field name used as unique key for each item */
|
|
9
|
+
keyField: keyof T & string;
|
|
10
|
+
/** Render function for each item */
|
|
11
|
+
renderItem: (item: T, index: number) => ReactNode;
|
|
12
|
+
/** Layout direction */
|
|
13
|
+
direction?: "row" | "column";
|
|
14
|
+
/** Container class name */
|
|
15
|
+
className?: string;
|
|
16
|
+
/** Class name applied to each item wrapper */
|
|
17
|
+
itemClassName?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function SortableList<T extends object>({ items, onReorder, keyField, renderItem, direction, className, itemClassName, }: SortableListProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import "../styles/SortableList.css";
|
|
3
|
+
import { clsx } from "clsx";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import { ReactSortable } from "react-sortablejs";
|
|
6
|
+
export function SortableList({ items, onReorder, keyField, renderItem, direction = "column", className, itemClassName, }) {
|
|
7
|
+
const [sortableItems, setSortableItems] = useState(() => items.map((item) => ({
|
|
8
|
+
id: String(item[keyField]),
|
|
9
|
+
data: item,
|
|
10
|
+
})));
|
|
11
|
+
// Sync external items to internal state
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
setSortableItems(items.map((item) => ({
|
|
14
|
+
id: String(item[keyField]),
|
|
15
|
+
data: item,
|
|
16
|
+
})));
|
|
17
|
+
}, [items, keyField]);
|
|
18
|
+
const handleSetList = (newList) => {
|
|
19
|
+
// Ignore empty list updates from ReactSortable during initialization
|
|
20
|
+
if (newList.length === 0 && sortableItems.length > 0) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
setSortableItems(newList);
|
|
24
|
+
onReorder(newList.map((item) => item.data));
|
|
25
|
+
};
|
|
26
|
+
return (_jsx(ReactSortable, { tag: "div", className: clsx("PaSortableList", direction, className), list: sortableItems, setList: handleSetList, children: sortableItems.map((item, index) => (_jsx("div", { className: clsx("PaSortableList-item", itemClassName), children: renderItem(item.data, index) }, item.id))) }));
|
|
27
|
+
}
|
package/dist/Spinner.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import "../styles/SplitButton.css";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
import { type AnchorElProps, type AsyncButtonElProps, type ButtonElProps } from "./Button.js";
|
|
4
|
+
export type SplitButtonProps = SplitButtonElProps | SplitAsyncButtonElProps | SplitAnchorElProps;
|
|
5
|
+
export interface SplitButtonElProps extends SplitButtonBaseProps, Omit<ButtonElProps, "className"> {
|
|
6
|
+
}
|
|
7
|
+
export interface SplitAsyncButtonElProps extends SplitButtonBaseProps, Omit<AsyncButtonElProps, "className"> {
|
|
8
|
+
}
|
|
9
|
+
export interface SplitAnchorElProps extends SplitButtonBaseProps, Omit<AnchorElProps, "className"> {
|
|
10
|
+
}
|
|
11
|
+
interface SplitButtonBaseProps {
|
|
12
|
+
items: SplitButtonItem[];
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface SplitButtonItem {
|
|
16
|
+
label: string;
|
|
17
|
+
icon?: ReactNode;
|
|
18
|
+
/** can be async */
|
|
19
|
+
command: () => unknown;
|
|
20
|
+
danger?: boolean;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function SplitButton(props: SplitButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "../styles/SplitButton.css";
|
|
3
|
+
import { clsx } from "clsx";
|
|
4
|
+
import { ChevronDown } from "lucide-react";
|
|
5
|
+
import { useEffect, useRef, useState } from "react";
|
|
6
|
+
import { Button, } from "./Button.js";
|
|
7
|
+
export function SplitButton(props) {
|
|
8
|
+
const { items, className, ...buttonProps } = props;
|
|
9
|
+
const [open, setOpen] = useState(false);
|
|
10
|
+
const containerRef = useRef(null);
|
|
11
|
+
const severity = props.severity ?? "primary";
|
|
12
|
+
const disabled = props.disabled;
|
|
13
|
+
// Close dropdown when clicking outside
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!open)
|
|
16
|
+
return;
|
|
17
|
+
const handleClickOutside = (e) => {
|
|
18
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
19
|
+
setOpen(false);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
23
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
24
|
+
}, [open]);
|
|
25
|
+
const handleItemClick = async (item) => {
|
|
26
|
+
try {
|
|
27
|
+
await item.command();
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (props.as === "async" && props.onError) {
|
|
31
|
+
props.onError(error);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
console.error("Error executing SplitButton item command:", error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
setOpen(false);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
return (_jsxs("div", { ref: containerRef, className: clsx("PaSplitBtn", severity, open && "open", disabled && "disabled", className), children: [_jsx(Button, { ...buttonProps, className: "PaSplitBtn-main" }), _jsx("button", { type: "button", className: "PaSplitBtn-toggle", onClick: () => setOpen(!open), "aria-haspopup": "menu", "aria-expanded": open, children: _jsx(ChevronDown, { className: "PaSplitBtn-toggleIcon" }) }), open && (_jsx("div", { className: "PaSplitBtn-menu", role: "menu", children: items.map((item) => (_jsxs("button", { type: "button", className: clsx("PaSplitBtn-menuItem", item.danger && "danger", item.disabled && "disabled"), onClick: () => void handleItemClick(item), role: "menuitem", disabled: item.disabled, children: [item.icon && _jsx("span", { className: "PaSplitBtn-menuIcon", children: item.icon }), item.label] }, item.label))) }))] }));
|
|
42
|
+
}
|
package/dist/Switch.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { InputHTMLAttributes } from "react";
|
|
2
|
+
import "../styles/Switch.css";
|
|
3
|
+
export interface SwitchProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "onChange"> {
|
|
4
|
+
checked: boolean;
|
|
5
|
+
onChange: (checked: boolean) => void;
|
|
6
|
+
label?: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function Switch({ checked, onChange, label, className, ...rest }: SwitchProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/Switch.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { clsx } from "clsx";
|
|
3
|
+
import "../styles/Switch.css";
|
|
4
|
+
export function Switch({ checked, onChange, label, className, ...rest }) {
|
|
5
|
+
const handleChange = (e) => {
|
|
6
|
+
onChange(e.target.checked);
|
|
7
|
+
};
|
|
8
|
+
if (label) {
|
|
9
|
+
return (_jsxs("label", { className: clsx("PaSwitch", className), children: [_jsx("input", { className: "PaSwitch-input", type: "checkbox", checked: checked, onChange: handleChange, ...rest }), _jsx("span", { className: "PaSwitch-track", children: _jsx("span", { className: "PaSwitch-thumb" }) }), _jsx("span", { className: "PaSwitch-label", children: label })] }));
|
|
10
|
+
}
|
|
11
|
+
return (_jsxs("span", { className: clsx("PaSwitch", className), children: [_jsx("input", { className: "PaSwitch-input", type: "checkbox", checked: checked, onChange: handleChange, ...rest }), _jsx("span", { className: "PaSwitch-track", children: _jsx("span", { className: "PaSwitch-thumb" }) })] }));
|
|
12
|
+
}
|
package/dist/Tabs.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import "../styles/Tabs.css";
|
|
3
|
+
export interface TabProps {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
icon?: ReactNode;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface TabsProps {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
value: string;
|
|
13
|
+
onChange: (value: string) => void;
|
|
14
|
+
variant?: "default" | "pill" | "stretch";
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function Tabs({ children, value, onChange, variant, className }: TabsProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
/**
|
|
19
|
+
* Render-less component for tab definition.
|
|
20
|
+
* Used for configuration only - does not render anything directly.
|
|
21
|
+
*/
|
|
22
|
+
export declare function Tab(_props: TabProps): null;
|
package/dist/Tabs.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { clsx } from "clsx";
|
|
3
|
+
import { Children, isValidElement } from "react";
|
|
4
|
+
import "../styles/Tabs.css";
|
|
5
|
+
export function Tabs({ children, value, onChange, variant = "default", className }) {
|
|
6
|
+
// Extract tab definitions from children
|
|
7
|
+
const tabs = [];
|
|
8
|
+
Children.forEach(children, (child) => {
|
|
9
|
+
if (isValidElement(child) && child.props) {
|
|
10
|
+
tabs.push(child.props);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
return (_jsx("div", { className: clsx("PaTabs", variant, className), role: "tablist", children: tabs.map((tab) => (_jsxs("button", { type: "button", className: clsx("PaTab", value === tab.value && "active", tab.disabled && "disabled", tab.className), onClick: () => !tab.disabled && onChange(tab.value), disabled: tab.disabled, role: "tab", "aria-selected": value === tab.value, children: [tab.icon && _jsx("span", { className: "PaTab-icon", children: tab.icon }), tab.label] }, tab.value))) }));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Render-less component for tab definition.
|
|
17
|
+
* Used for configuration only - does not render anything directly.
|
|
18
|
+
*/
|
|
19
|
+
export function Tab(_props) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "../styles/Textarea.css";
|
|
2
|
+
import type { Ref, TextareaHTMLAttributes } from "react";
|
|
3
|
+
export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
4
|
+
label?: string;
|
|
5
|
+
error?: string;
|
|
6
|
+
ref?: Ref<HTMLTextAreaElement>;
|
|
7
|
+
}
|
|
8
|
+
export declare function Textarea({ className, label, error, ref, ...props }: TextareaProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/Textarea.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "../styles/Textarea.css";
|
|
3
|
+
import { clsx } from "clsx";
|
|
4
|
+
export function Textarea({ className, label, error, ref, ...props }) {
|
|
5
|
+
const textareaElement = (_jsx("textarea", { ref: ref, className: clsx("PaTextarea-field", error && "error"), ...props }));
|
|
6
|
+
return (_jsxs("span", { className: clsx("PaTextarea", className), children: [label ? (_jsxs("label", { className: "PaTextarea-wrapper", children: [_jsx("span", { className: "PaTextarea-label", children: label }), textareaElement] })) : (textareaElement), error && _jsx("span", { className: "PaTextarea-error", children: error })] }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import "../styles/ToggleButton.css";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
export interface ToggleButtonProps {
|
|
4
|
+
checked: boolean;
|
|
5
|
+
onChange: (checked: boolean) => void;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
"aria-label"?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function ToggleButton({ checked, onChange, children, className, disabled, ...rest }: ToggleButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import "../styles/ToggleButton.css";
|
|
3
|
+
import { clsx } from "clsx";
|
|
4
|
+
export function ToggleButton({ checked, onChange, children, className, disabled, ...rest }) {
|
|
5
|
+
return (_jsx("button", { type: "button", className: clsx("PaToggleBtn", checked && "active", disabled && "disabled", className), onClick: () => !disabled && onChange(!checked), disabled: disabled, "aria-pressed": checked, ...rest, children: children }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import "../styles/ToggleGroup.css";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
export interface ToggleGroupOption {
|
|
4
|
+
value: string;
|
|
5
|
+
label: ReactNode;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface ToggleGroupProps {
|
|
9
|
+
value: string;
|
|
10
|
+
onChange: (value: string) => void;
|
|
11
|
+
options: ToggleGroupOption[];
|
|
12
|
+
className?: string;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function ToggleGroup({ value, onChange, options, className, disabled }: ToggleGroupProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import "../styles/ToggleGroup.css";
|
|
3
|
+
import { clsx } from "clsx";
|
|
4
|
+
export function ToggleGroup({ value, onChange, options, className, disabled }) {
|
|
5
|
+
return (_jsx("fieldset", { className: clsx("PaToggleGroup", disabled && "disabled", className), disabled: disabled, children: options.map((option) => (_jsx("button", { type: "button", className: clsx("PaToggleGroup-item", value === option.value && "active", option.disabled && "disabled"), onClick: () => !option.disabled && onChange(option.value), disabled: option.disabled, "aria-pressed": value === option.value, children: option.label }, option.value))) }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import "../styles/Tooltip.css";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
export interface TooltipProps {
|
|
4
|
+
content: string;
|
|
5
|
+
position?: "top" | "bottom" | "left" | "right";
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
delay?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function Tooltip({ content, position, children, className, delay, }: TooltipProps): import("react/jsx-runtime").JSX.Element;
|