periplo-ui 3.15.1 → 3.16.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/README.md CHANGED
@@ -74,6 +74,37 @@
74
74
 
75
75
  - Refer to documentation for individual component usage and API reference: https://periplo-ui.vercel.app/?path=/docs/getting-started--docs
76
76
 
77
+ ## 🔧 Local Storybook Package Installation
78
+
79
+ You can test this Storybook package locally before publishing by following these steps:
80
+
81
+ ### 📦 In the Storybook project:
82
+
83
+ 1. **Install dependencies:**
84
+ ```bash
85
+ npm install
86
+ ```
87
+
88
+ 2. **Build the project (if needed):**
89
+ ```bash
90
+ npm run build
91
+ ```
92
+
93
+ 3. **Package the project:**
94
+ This will generate a .tgz file in the project root.
95
+ ```bash
96
+ npm pack
97
+ ```
98
+
99
+ ### 📥 In the target project:
100
+
101
+ 1. **Install the package locally** (replace the path with your .tgz file path):
102
+ ```bash
103
+ npm install /path/to/your/file/periplo-ui-1.0.0.tgz --force
104
+ ```
105
+
106
+ ⚠️ Make sure to use the correct absolute or relative path to the .tgz file generated with npm pack.
107
+
77
108
  ## Available Commands:
78
109
 
79
110
  | Command | Description |
@@ -0,0 +1,62 @@
1
+ import { ControllerFieldState } from 'react-hook-form';
2
+ import * as React from 'react';
3
+ /** Represents a selectable option in the InlineMultiSelect */
4
+ type Option = {
5
+ value: string;
6
+ label: string;
7
+ };
8
+ /** Props for the InlineMultiSelect component */
9
+ export type InlineMultiSelectProps = {
10
+ /** Array of options to display in the select */
11
+ options: Array<Option>;
12
+ /** Currently selected values (controlled) */
13
+ value?: Array<string>;
14
+ /** Callback fired when selection changes */
15
+ onChange?: (value: string[]) => void;
16
+ /** Form field state from react-hook-form */
17
+ fieldState?: ControllerFieldState;
18
+ /** Default selected values (uncontrolled) */
19
+ defaultValue?: Array<string>;
20
+ /** Placeholder text when no items are selected */
21
+ placeholder?: string;
22
+ /** Text to show when no options match the search */
23
+ notFoundText?: string;
24
+ /** Placeholder for the search input */
25
+ commandInputPlaceholder?: string;
26
+ /** Maximum number of items that can be selected */
27
+ max?: number;
28
+ /** Additional CSS classes */
29
+ className?: string;
30
+ };
31
+ /**
32
+ * InlineMultiSelect Component
33
+ *
34
+ * A controlled multi-select component that displays selected items inline with search functionality.
35
+ * Supports keyboard navigation, maximum selection limit, and custom styling.
36
+ *
37
+ * @example
38
+ * ```tsx
39
+ * // Controlled usage
40
+ * const [value, setValue] = useState<string[]>([])
41
+ * <InlineMultiSelect
42
+ * options={[{ value: '1', label: 'Option 1' }]}
43
+ * value={value}
44
+ * onChange={setValue}
45
+ * />
46
+ *
47
+ * // With react-hook-form
48
+ * const { control } = useForm()
49
+ * <Controller
50
+ * name="myField"
51
+ * control={control}
52
+ * render={({ field }) => (
53
+ * <InlineMultiSelect
54
+ * {...field}
55
+ * options={options}
56
+ * />
57
+ * )}
58
+ * />
59
+ * ```
60
+ */
61
+ export declare const InlineMultiSelect: React.ForwardRefExoticComponent<InlineMultiSelectProps & React.RefAttributes<HTMLInputElement>>;
62
+ export {};
@@ -0,0 +1,173 @@
1
+ "use client";
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from '../Command/Command.js';
5
+ import { X, CaretUpDown, Check } from '@phosphor-icons/react';
6
+ import { PopoverRoot, PopoverTrigger, PopoverContent } from '../Popover/Popover.js';
7
+ import { cn } from '../../lib/utils.js';
8
+ import { Button } from '../Button/Button.js';
9
+
10
+ const InlineMultiSelect = React.forwardRef(
11
+ ({
12
+ options,
13
+ value,
14
+ onChange,
15
+ defaultValue,
16
+ placeholder,
17
+ notFoundText,
18
+ fieldState,
19
+ commandInputPlaceholder,
20
+ max,
21
+ className
22
+ }, _ref) => {
23
+ const inputRef = React.useRef(null);
24
+ const containerRef = React.useRef(null);
25
+ const [open, setOpen] = React.useState(false);
26
+ const [inputValue, setInputValue] = React.useState("");
27
+ const [visibleItems, setVisibleItems] = React.useState([]);
28
+ const [hiddenCount, setHiddenCount] = React.useState(0);
29
+ const [internalSelected, setInternalSelected] = React.useState(
30
+ options.filter((option) => defaultValue?.includes(option.value) ?? [])
31
+ );
32
+ const selected = value ? options.filter((option) => value.includes(option.value)) : internalSelected;
33
+ const handleSelectionChange = React.useCallback(
34
+ (newSelected) => {
35
+ if (!value) {
36
+ setInternalSelected(newSelected);
37
+ }
38
+ onChange?.(newSelected.map((s) => s.value));
39
+ },
40
+ [value, onChange]
41
+ );
42
+ const removeItem = React.useCallback(
43
+ (itemToRemove) => {
44
+ const newSelected = itemToRemove ? selected.filter((s) => s.value !== itemToRemove.value) : selected.slice(0, -1);
45
+ handleSelectionChange(newSelected);
46
+ },
47
+ [selected, handleSelectionChange]
48
+ );
49
+ const handleUnselect = React.useCallback(
50
+ (item, e) => {
51
+ e.stopPropagation();
52
+ e.preventDefault();
53
+ removeItem(item);
54
+ },
55
+ [removeItem]
56
+ );
57
+ const handleKeyDown = React.useCallback(
58
+ (e) => {
59
+ const input = inputRef.current;
60
+ if (input) {
61
+ if ((e.key === "Delete" || e.key === "Backspace") && input.value === "") {
62
+ removeItem();
63
+ }
64
+ if (e.key === "Escape") {
65
+ input.blur();
66
+ }
67
+ }
68
+ },
69
+ [removeItem]
70
+ );
71
+ React.useEffect(() => {
72
+ const updateVisibleItems = () => {
73
+ if (!containerRef.current || selected.length === 0) return;
74
+ const maxVisibleItems = 2;
75
+ const visibleCount = Math.min(selected.length, maxVisibleItems);
76
+ setVisibleItems(selected.slice(0, visibleCount));
77
+ setHiddenCount(selected.length - visibleCount);
78
+ };
79
+ updateVisibleItems();
80
+ window.addEventListener("resize", updateVisibleItems);
81
+ return () => window.removeEventListener("resize", updateVisibleItems);
82
+ }, [selected]);
83
+ return /* @__PURE__ */ jsx(PopoverRoot, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(Command, { onKeyDown: handleKeyDown, children: [
84
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
85
+ Button,
86
+ {
87
+ variant: "ghost",
88
+ "aria-expanded": open,
89
+ className: cn(
90
+ "w-full justify-between bg-[#fff] pl-2 pr-2",
91
+ fieldState?.invalid ? "border-error-400 focus-within:border-error-700" : "",
92
+ className
93
+ ),
94
+ children: [
95
+ /* @__PURE__ */ jsx("div", { ref: containerRef, className: "flex flex-1 flex-wrap items-center gap-1 overflow-hidden pr-2", children: /* @__PURE__ */ jsx("div", { className: "flex items-center whitespace-nowrap", children: selected.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
96
+ visibleItems.map((item) => /* @__PURE__ */ jsxs("div", { className: "bg-muted flex items-center gap-1 rounded px-1 py-0.5", children: [
97
+ /* @__PURE__ */ jsx("span", { className: "text-sm", children: item.label }),
98
+ /* @__PURE__ */ jsx(
99
+ "span",
100
+ {
101
+ role: "button",
102
+ tabIndex: 0,
103
+ onClick: (e) => handleUnselect(item, e),
104
+ onKeyDown: (e) => {
105
+ if (e.key === "Enter" || e.key === " ") {
106
+ handleUnselect(item, e);
107
+ }
108
+ },
109
+ "aria-label": `Eliminar ${item.label}`,
110
+ className: "hover:bg-muted-foreground/20 flex h-4 w-4 items-center justify-center rounded-full",
111
+ children: /* @__PURE__ */ jsx(X, { className: "text-muted-foreground h-3 w-3" })
112
+ }
113
+ )
114
+ ] }, item.value)),
115
+ hiddenCount > 0 && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground inline-flex flex-shrink-0 items-center whitespace-nowrap text-sm", children: [
116
+ "+",
117
+ hiddenCount
118
+ ] })
119
+ ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: placeholder }) }) }),
120
+ /* @__PURE__ */ jsx(CaretUpDown, { className: "h-4 w-4 shrink-0 opacity-50" })
121
+ ]
122
+ }
123
+ ) }),
124
+ /* @__PURE__ */ jsxs(PopoverContent, { className: "px-0 py-0", children: [
125
+ /* @__PURE__ */ jsx(
126
+ CommandInput,
127
+ {
128
+ placeholder: commandInputPlaceholder ?? "Search...",
129
+ value: inputValue,
130
+ onValueChange: setInputValue,
131
+ children: max && /* @__PURE__ */ jsxs("div", { className: "bg-muted rounded-md px-2 py-1 text-sm", children: [
132
+ selected.length,
133
+ " / ",
134
+ max
135
+ ] })
136
+ }
137
+ ),
138
+ /* @__PURE__ */ jsxs(CommandList, { children: [
139
+ /* @__PURE__ */ jsx(CommandEmpty, { children: notFoundText ?? "Options not found" }),
140
+ open && options.length > 0 && /* @__PURE__ */ jsx(CommandGroup, { children: options.map((item) => /* @__PURE__ */ jsx(
141
+ CommandItem,
142
+ {
143
+ onMouseDown: (e) => {
144
+ e.preventDefault();
145
+ e.stopPropagation();
146
+ },
147
+ onSelect: () => {
148
+ const isSelected = selected.find((s) => s.value === item.value);
149
+ if (isSelected) {
150
+ handleSelectionChange(selected.filter((s) => s.value !== item.value));
151
+ } else {
152
+ if (max && selected.length >= max) return;
153
+ setInputValue("");
154
+ handleSelectionChange([...selected, item]);
155
+ }
156
+ },
157
+ className: "cursor-pointer",
158
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
159
+ /* @__PURE__ */ jsx("div", { className: "mr-2", children: selected.find((s) => s.value === item.value) ? /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx("div", { className: "h-4 w-4" }) }),
160
+ /* @__PURE__ */ jsx("div", { children: item.label })
161
+ ] })
162
+ },
163
+ `item-${item.value}`
164
+ )) })
165
+ ] })
166
+ ] })
167
+ ] }) });
168
+ }
169
+ );
170
+ InlineMultiSelect.displayName = "InlineMultiSelect";
171
+
172
+ export { InlineMultiSelect };
173
+ //# sourceMappingURL=InlineMultiSelect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InlineMultiSelect.js","sources":["../../../src/components/InlineMultiSelect/InlineMultiSelect.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '../Command'\nimport { CaretUpDown, Check, X } from '@phosphor-icons/react'\nimport { PopoverRoot, PopoverContent, PopoverTrigger } from '../Popover'\nimport { ControllerFieldState } from 'react-hook-form'\nimport { cn } from '@/lib/utils'\nimport { Button } from '../Button'\n\n/** Represents a selectable option in the InlineMultiSelect */\ntype Option = { value: string; label: string }\n\n/** Props for the InlineMultiSelect component */\nexport type InlineMultiSelectProps = {\n /** Array of options to display in the select */\n options: Array<Option>\n /** Currently selected values (controlled) */\n value?: Array<string>\n /** Callback fired when selection changes */\n onChange?: (value: string[]) => void\n /** Form field state from react-hook-form */\n fieldState?: ControllerFieldState\n /** Default selected values (uncontrolled) */\n defaultValue?: Array<string>\n /** Placeholder text when no items are selected */\n placeholder?: string\n /** Text to show when no options match the search */\n notFoundText?: string\n /** Placeholder for the search input */\n commandInputPlaceholder?: string\n /** Maximum number of items that can be selected */\n max?: number\n /** Additional CSS classes */\n className?: string\n}\n\n/**\n * InlineMultiSelect Component\n *\n * A controlled multi-select component that displays selected items inline with search functionality.\n * Supports keyboard navigation, maximum selection limit, and custom styling.\n *\n * @example\n * ```tsx\n * // Controlled usage\n * const [value, setValue] = useState<string[]>([])\n * <InlineMultiSelect\n * options={[{ value: '1', label: 'Option 1' }]}\n * value={value}\n * onChange={setValue}\n * />\n *\n * // With react-hook-form\n * const { control } = useForm()\n * <Controller\n * name=\"myField\"\n * control={control}\n * render={({ field }) => (\n * <InlineMultiSelect\n * {...field}\n * options={options}\n * />\n * )}\n * />\n * ```\n */\nexport const InlineMultiSelect = React.forwardRef<HTMLInputElement, InlineMultiSelectProps>(\n (\n {\n options,\n value,\n onChange,\n defaultValue,\n placeholder,\n notFoundText,\n fieldState,\n commandInputPlaceholder,\n max,\n className,\n },\n _ref,\n ) => {\n const inputRef = React.useRef<HTMLInputElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [open, setOpen] = React.useState(false)\n const [inputValue, setInputValue] = React.useState('')\n const [visibleItems, setVisibleItems] = React.useState<Option[]>([])\n const [hiddenCount, setHiddenCount] = React.useState(0)\n\n // Use controlled value if provided, otherwise use internal state\n const [internalSelected, setInternalSelected] = React.useState<InlineMultiSelectProps['options']>(\n options.filter((option) => defaultValue?.includes(option.value) ?? []),\n )\n\n const selected = value ? options.filter((option) => value.includes(option.value)) : internalSelected\n\n const handleSelectionChange = React.useCallback(\n (newSelected: Option[]) => {\n if (!value) {\n // Uncontrolled mode\n setInternalSelected(newSelected)\n }\n // Always call onChange with the new values\n onChange?.(newSelected.map((s) => s.value))\n },\n [value, onChange],\n )\n\n /**\n * Removes an item from the selection\n * @param itemToRemove - Optional item to remove. If not provided, removes the last item\n */\n const removeItem = React.useCallback(\n (itemToRemove?: Option) => {\n const newSelected = itemToRemove\n ? selected.filter((s) => s.value !== itemToRemove.value)\n : selected.slice(0, -1)\n\n handleSelectionChange(newSelected)\n },\n [selected, handleSelectionChange],\n )\n\n /**\n * Handles the unselect action when clicking the remove button\n * @param item - The item to remove\n * @param e - The event object\n */\n const handleUnselect = React.useCallback(\n (item: Option, e: React.MouseEvent | React.KeyboardEvent) => {\n e.stopPropagation()\n e.preventDefault()\n removeItem(item)\n },\n [removeItem],\n )\n\n /**\n * Handles keyboard events for the component\n * - Delete/Backspace: Removes last item when input is empty\n * - Escape: Blurs the input\n */\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLDivElement>) => {\n const input = inputRef.current\n if (input) {\n if ((e.key === 'Delete' || e.key === 'Backspace') && input.value === '') {\n removeItem()\n }\n if (e.key === 'Escape') {\n input.blur()\n }\n }\n },\n [removeItem],\n )\n\n /**\n * Effect to update visible items and handle responsive behavior\n * Shows a maximum of 2 items and displays a count for the rest\n */\n React.useEffect(() => {\n const updateVisibleItems = () => {\n if (!containerRef.current || selected.length === 0) return\n\n const maxVisibleItems = 2\n const visibleCount = Math.min(selected.length, maxVisibleItems)\n\n setVisibleItems(selected.slice(0, visibleCount))\n setHiddenCount(selected.length - visibleCount)\n }\n\n updateVisibleItems()\n window.addEventListener('resize', updateVisibleItems)\n return () => window.removeEventListener('resize', updateVisibleItems)\n }, [selected])\n\n return (\n <PopoverRoot open={open} onOpenChange={setOpen}>\n <Command onKeyDown={handleKeyDown}>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n aria-expanded={open}\n className={cn(\n 'w-full justify-between bg-[#fff] pl-2 pr-2',\n fieldState?.invalid ? 'border-error-400 focus-within:border-error-700' : '',\n className,\n )}\n >\n <div ref={containerRef} className=\"flex flex-1 flex-wrap items-center gap-1 overflow-hidden pr-2\">\n <div className=\"flex items-center whitespace-nowrap\">\n {selected.length > 0 ? (\n <>\n {visibleItems.map((item) => (\n <div key={item.value} className=\"bg-muted flex items-center gap-1 rounded px-1 py-0.5\">\n <span className=\"text-sm\">{item.label}</span>\n <span\n role=\"button\"\n tabIndex={0}\n onClick={(e) => handleUnselect(item, e)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n handleUnselect(item, e)\n }\n }}\n aria-label={`Eliminar ${item.label}`}\n className=\"hover:bg-muted-foreground/20 flex h-4 w-4 items-center justify-center rounded-full\"\n >\n <X className=\"text-muted-foreground h-3 w-3\" />\n </span>\n </div>\n ))}\n {hiddenCount > 0 && (\n <span className=\"text-muted-foreground inline-flex flex-shrink-0 items-center whitespace-nowrap text-sm\">\n +{hiddenCount}\n </span>\n )}\n </>\n ) : (\n <span className=\"text-muted-foreground\">{placeholder}</span>\n )}\n </div>\n </div>\n <CaretUpDown className=\"h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"px-0 py-0\">\n <CommandInput\n placeholder={commandInputPlaceholder ?? 'Search...'}\n value={inputValue}\n onValueChange={setInputValue}\n >\n {max && (\n <div className=\"bg-muted rounded-md px-2 py-1 text-sm\">\n {selected.length} / {max}\n </div>\n )}\n </CommandInput>\n <CommandList>\n <CommandEmpty>{notFoundText ?? 'Options not found'}</CommandEmpty>\n {open && options.length > 0 && (\n <CommandGroup>\n {options.map((item) => (\n <CommandItem\n key={`item-${item.value}`}\n onMouseDown={(e) => {\n e.preventDefault()\n e.stopPropagation()\n }}\n onSelect={() => {\n const isSelected = selected.find((s) => s.value === item.value)\n if (isSelected) {\n handleSelectionChange(selected.filter((s) => s.value !== item.value))\n } else {\n if (max && selected.length >= max) return\n setInputValue('')\n handleSelectionChange([...selected, item])\n }\n }}\n className=\"cursor-pointer\"\n >\n <div className=\"flex items-center\">\n <div className=\"mr-2\">\n {selected.find((s) => s.value === item.value) ? (\n <Check className=\"h-4 w-4\" />\n ) : (\n <div className=\"h-4 w-4\" />\n )}\n </div>\n <div>{item.label}</div>\n </div>\n </CommandItem>\n ))}\n </CommandGroup>\n )}\n </CommandList>\n </PopoverContent>\n </Command>\n </PopoverRoot>\n )\n },\n)\n\nInlineMultiSelect.displayName = 'InlineMultiSelect'\n"],"names":[],"mappings":";;;;;;;;;AAmEO;AAAgC;AAEnC;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIF;AACA;AACA;AACA;AACA;AACA;AAGA;AAAsD;AACiB;AAGvE;AAEA;AAAoC;AAEhC;AAEE;AAA+B;AAGjC;AAA0C;AAC5C;AACgB;AAOlB;AAAyB;AAErB;AAIA;AAAiC;AACnC;AACgC;AAQlC;AAA6B;AAEzB;AACA;AACA;AAAe;AACjB;AACW;AAQb;AAA4B;AAExB;AACA;AACE;AACE;AAAW;AAEb;AACE;AAAW;AACb;AACF;AACF;AACW;AAOb;AACE;AACE;AAEA;AACA;AAEA;AACA;AAA6C;AAG/C;AACA;AACA;AAAoE;AAGtE;AAGM;AACE;AAAC;AAAA;AACS;AACO;AACJ;AACT;AACyE;AACzE;AACF;AAEA;AAIS;AAEG;AAAsC;AACtC;AAAC;AAAA;AACM;AACK;AAC4B;AAEpC;AACE;AAAsB;AACxB;AACF;AACkC;AACxB;AAEmC;AAAA;AAC/C;AAEH;AAE0G;AAAA;AACrG;AACJ;AAOV;AACqD;AAAA;AAAA;AAEzD;AAEE;AAAA;AAAC;AAAA;AACyC;AACjC;AACQ;AAIV;AAAS;AAAO;AAAI;AACvB;AAAA;AAEJ;AAEE;AAAmD;AAI7C;AAAC;AAAA;AAGG;AACA;AAAkB;AACpB;AAEE;AACA;AACE;AAAoE;AAEpE;AACA;AACA;AAAyC;AAC3C;AACF;AACU;AAGR;AAMA;AACiB;AACnB;AAAA;AA1BuB;AA6B7B;AAEJ;AACF;AAEJ;AAGN;AAEA;;"}
@@ -0,0 +1 @@
1
+ export * from './InlineMultiSelect';
@@ -0,0 +1,2 @@
1
+ export { InlineMultiSelect } from './InlineMultiSelect.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/dist/index.d.ts CHANGED
@@ -39,3 +39,4 @@ export * from './components/Combobox';
39
39
  export * from './components/Table';
40
40
  export * from './components/DataTable';
41
41
  export * from './components/Collapsible';
42
+ export * from './components/InlineMultiSelect';
package/dist/index.js CHANGED
@@ -38,4 +38,5 @@ export { SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, Sideba
38
38
  export { Combobox } from './components/Combobox/Combobox.js';
39
39
  export { DataTable } from './components/DataTable/DataTable.js';
40
40
  export { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from './components/Collapsible/Collapsible.js';
41
+ export { InlineMultiSelect } from './components/InlineMultiSelect/InlineMultiSelect.js';
41
42
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "periplo-ui",
3
3
  "description": "IATI UI library",
4
4
  "private": false,
5
- "version": "3.15.1",
5
+ "version": "3.16.0",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",