- {slottedItems.map(({ itemId, slotId, item }, index) => (
-
onChange(newIndex === -1 ? arrayRemove(items, oldIndex) : arrayMove(items, oldIndex, newIndex))}
+ renderList={({ children, props }) => {
+ const { key, ...rest } = props;
+
+ return (
+
- {item && (
-
- {children({
- ...item,
- updateData: (newValue) => {
- onChange(items.map((i) => (i.id === itemId ? { ...i, ...newValue } : i)));
- },
- itemIndex: index,
- deleteItem: () => {
- onChange(items.filter((i) => i.id !== item.id));
-
- if (item.onAfterItemRemove) {
- onAfterItemRemove(item);
- }
- },
- })}
-
+ {children}
+
+ );
+ }}
+ renderItem={({ value, index, isDisabled, isDragged, isSelected, isOutOfBounds, props }) => {
+ const { key, ...rest } = props;
+
+ return (
+
- ))}
-
-
+ key={value?.id ?? key}
+ {...rest}
+ >
+ {children({
+ ...value,
+ updateData: (newValue) => {
+ onChange(items.map((i) => (i.id === value.id ? { ...i, ...newValue } : i)));
+ },
+ itemIndex: index,
+ deleteItem: () => {
+ onChange(items.filter((i) => i.id !== value.id));
+
+ if (value.onAfterItemRemove) {
+ onAfterItemRemove(value);
+ }
+ },
+ })}
+
+ );
+ }}
+ />
);
};
diff --git a/lib/components/draggable/draggable-handle.jsx b/lib/components/draggable/draggable-handle.jsx
new file mode 100644
index 0000000..32923d4
--- /dev/null
+++ b/lib/components/draggable/draggable-handle.jsx
@@ -0,0 +1,43 @@
+import { clsx } from 'clsx/lite';
+import { __ } from '@wordpress/i18n';
+import { DraggableContext } from './draggable-context';
+import { useContext } from 'react';
+import { icons } from '../../icons';
+
+/**
+ * A Draggable item handle.
+ *
+ * @component
+ * @param {Object} props - Component props.
+ * @param {string} [props.className] - Classes to pass to the handle.
+ *
+ * @returns {JSX.Element} The DraggableHandle component.
+ *
+ * @example
+ *
+ *
+ * @preserve
+ */
+export const DraggableHandle = (props) => {
+ const { className, children, ...rest } = props;
+
+ const { handleRef, status } = useContext(DraggableContext);
+
+ return (
+
svg]:es-uic-size-4 [&>svg]:es-uic-shrink-0',
+ 'active:es-uic-border-teal-500/30 active:es-uic-bg-teal-50 active:es-uic-text-teal-500',
+ 'focus:es-uic-outline-none focus-visible:es-uic-border-teal-500 focus-visible:es-uic-outline-none focus-visible:es-uic-ring focus-visible:es-uic-ring-teal-500 focus-visible:es-uic-ring-opacity-50',
+ 'hover:es-uic-text-teal-500',
+ status === 'dragging' && '!es-uic-border-teal-600 !es-uic-bg-teal-500 !es-uic-text-white es-uic-shadow-sm es-uic-shadow-teal-500/30',
+ className,
+ )}
+ ref={handleRef}
+ {...rest}
+ >
+ {children ?? icons.reorderGrabberV}
+
+ );
+};
diff --git a/lib/components/draggable/draggable-item.jsx b/lib/components/draggable/draggable-item.jsx
deleted file mode 100644
index 9c1fa3a..0000000
--- a/lib/components/draggable/draggable-item.jsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { clsx } from 'clsx/lite';
-import { __ } from '@wordpress/i18n';
-import { DraggableContext } from './draggable-context';
-import { useContext } from 'react';
-import { icons } from '../../icons';
-
-/**
- * A Draggable item.
- *
- * @component
- * @param {Object} props - Component props.
- * @param {string} [props.className] - Classes to pass to the container.
- *
- * @returns {JSX.Element} The DraggableList component.
- *
- * @see {@link DraggableList} for usage example.
- *
- * @preserve
- */
-export const DraggableItem = (props) => {
- const { children, className, ...rest } = props;
-
- const { itemId } = useContext(DraggableContext);
-
- return (
-
- {children}
-
- );
-};
-
-/**
- * A Draggable item handle.
- *
- * @component
- * @param {Object} props - Component props.
- * @param {string} [props.className] - Classes to pass to the handle.
- *
- * @returns {JSX.Element} The DraggableItemHandle component.
- *
- * @example
- *
- *
- * @preserve
- */
-export const DraggableItemHandle = (props) => {
- const { className, children, ...rest } = props;
-
- return (
-
svg]:es-uic-size-4',
- className,
- )}
- {...rest}
- >
- {children ?? icons.reorderGrabberV}
-
- );
-};
diff --git a/lib/components/draggable/draggable.jsx b/lib/components/draggable/draggable.jsx
index 69390da..624c628 100644
--- a/lib/components/draggable/draggable.jsx
+++ b/lib/components/draggable/draggable.jsx
@@ -1,8 +1,11 @@
-import { useEffect, useId, useRef, useState, useMemo } from 'react';
+import { useEffect, useId, useRef, useState } from 'react';
import { __ } from '@wordpress/i18n';
-import { createSwapy } from 'swapy';
import { DraggableContext } from './draggable-context';
-import { clsx } from 'clsx/lite';
+import { useSortable } from '@dnd-kit/react/sortable';
+import { RestrictToHorizontalAxis, RestrictToVerticalAxis } from '@dnd-kit/abstract/modifiers';
+import { RestrictToElement } from '@dnd-kit/dom/modifiers';
+import { DragDropProvider } from '@dnd-kit/react';
+import { move } from '@dnd-kit/helpers';
const fixIds = (items, itemIdBase) => {
return items.map((item, i) => ({
@@ -11,6 +14,32 @@ const fixIds = (items, itemIdBase) => {
}));
};
+const SortableItem = ({ id, index, disabled, children, axis }) => {
+ const [element, setElement] = useState(null);
+ const handleRef = useRef(null);
+
+ let extraModifiers = [];
+
+ if (axis === 'horizontal') {
+ extraModifiers = [RestrictToHorizontalAxis];
+ } else if (axis === 'vertical') {
+ extraModifiers = [RestrictToVerticalAxis];
+ }
+
+ const { isDragSource, status } = useSortable({
+ id,
+ index,
+ element,
+ type: 'item',
+ modifiers: [RestrictToElement],
+ transition: { idle: true, duration: 400, easing: 'cubic-bezier(0, 0.55, 0.45, 1)' }, // 'easeOutCirc'
+ handle: handleRef,
+ disabled,
+ });
+
+ return
{children(handleRef, isDragSource, status)}
;
+};
+
/**
* A component that allows re-ordering items freely.
*
@@ -20,8 +49,8 @@ const fixIds = (items, itemIdBase) => {
* @param {Function} props.onChange - Function to run when the items change.
* @param {boolean} [props.hidden] - If `true`, the component is not rendered.
* @param {boolean} [props.noReorder] - If `true`, item reordering is disabled.
+ * @param {'both' | 'horizontal' | 'vertical'} [props.axis='both'] - Which axis to allow dragging on.
* @param {string} [props.className] - Classes to pass to the component.
- * @param {string} [props.slotClassName] - Classes to pass to the item container slot.
*
* @returns {JSX.Element} The Draggable component.
*
@@ -34,17 +63,11 @@ const fixIds = (items, itemIdBase) => {
* const { title, updateData } = item;
*
* return (
- *
- * updateData({ title: value })}
- * />
- *
+ *
+ *
+ *
+ * {title}
+ *
* );
* }}
*
@@ -62,78 +85,20 @@ export const Draggable = (props) => {
noReorder,
+ axis = 'both',
+
className,
- slotClassName,
hidden,
...rest
} = props;
- const items = useMemo(() => fixIds(rawItems, itemIdBase), [rawItems]);
-
- const ref = useRef(null);
- const swapyRef = useRef(null);
-
- const [slotItemsMap, setSlotItemsMap] = useState([
- ...items.map((item) => ({
- slotId: item.id,
- itemId: item.id,
- })),
- ]);
-
- const slottedItems = useMemo(
- () =>
- slotItemsMap.map(({ slotId, itemId }) => ({
- slotId,
- itemId,
- item: items.find((item) => item.id === itemId),
- })),
- [items, slotItemsMap],
- );
-
- // Keep Swapy slots in sync with items.
- useEffect(() => {
- const newItems = items
- .filter((item) => !slotItemsMap.some((slotItem) => slotItem.itemId === item.id))
- .map((item) => ({
- slotId: item.id,
- itemId: item.id,
- }));
-
- // Remove items from slotItemsMap if they no longer exist in items
- const withoutRemovedItems = slotItemsMap.filter((slotItem) => items.some((item) => item.id === slotItem.itemId) || !slotItem.itemId);
-
- const updatedSlotItemsMap = [...withoutRemovedItems, ...newItems];
-
- setSlotItemsMap(updatedSlotItemsMap);
- swapyRef.current?.setData({ array: updatedSlotItemsMap });
- }, [items]);
-
- // Initialize Swapy.
- useEffect(() => {
- const container = ref?.current;
-
- swapyRef.current = createSwapy(container, {
- manualSwap: true,
- });
-
- swapyRef.current.onSwap(({ data }) => {
- const tweakedItems = data.array.filter(({ itemId }) => itemId !== null).map(({ itemId }) => items.find((item) => item?.id === itemId));
- onChange(tweakedItems);
-
- // Set data manually.
- swapyRef.current?.setData({ array: data.array });
- setSlotItemsMap(data.array);
- });
-
- return () => {
- swapyRef.current?.destroy();
- };
- }, []);
+ const [items, setItems] = useState(fixIds(rawItems));
+ // Ensure the internal state is updated if items are updated externally.
useEffect(() => {
- swapyRef?.current?.enable(!noReorder);
- }, [noReorder]);
+ setItems(fixIds(rawItems, itemIdBase));
+ }, [rawItems]);
if (hidden) {
return null;
@@ -142,41 +107,66 @@ export const Draggable = (props) => {
return (
- {slottedItems.map(({ itemId, slotId, item }, index) => (
-
- {item && (
-
- {children({
- ...item,
- updateData: (newValue) => {
- onChange(items.map((i) => (i.id === itemId ? { ...i, ...newValue } : i)));
- },
- itemIndex: index,
- deleteItem: () => {
- onChange(items.filter((i) => i.id !== item.id));
-
- if (item.onAfterItemRemove) {
- onAfterItemRemove(item);
- }
- },
- })}
-
- )}
-
- ))}
+
{
+ if (!source || !target) {
+ return;
+ }
+
+ setItems((items) => move(items, source, target));
+ }}
+ onDragEnd={({ operation: { source, target }, canceled }) => {
+ if (canceled) {
+ return;
+ }
+
+ if (!source || !target) {
+ return;
+ }
+
+ setItems((items) => move(items, source, target));
+ onChange(items);
+ }}
+ >
+ {items.map((item, index) => (
+
+ {(handleRef, isDragSource, status) => (
+
+ {children({
+ ...item,
+ updateData: (newValue) => {
+ const updated = [...items].map((i) => (i.id === item.id ? { ...i, ...newValue } : i));
+
+ onChange(updated);
+ setItems(updated);
+ },
+ itemIndex: index,
+ deleteItem: () => {
+ setItems([...items].filter((i) => i.id !== item.id));
+ onChange([...items].filter((i) => i.id !== item.id));
+
+ if (onAfterItemRemove) {
+ onAfterItemRemove(item);
+ }
+ },
+ })}
+
+ )}
+
+ ))}
+
);
};
diff --git a/lib/components/expandable/expandable.jsx b/lib/components/expandable/expandable.jsx
index 5ab65a4..3f88d92 100644
--- a/lib/components/expandable/expandable.jsx
+++ b/lib/components/expandable/expandable.jsx
@@ -4,7 +4,6 @@ import { AnimatedVisibility } from '../animated-visibility/animated-visibility';
import { Button } from '../button/button';
import { icons } from '../../icons/icons';
import { clsx } from 'clsx/lite';
-
import { __ } from '@wordpress/i18n';
import { Label } from 'react-aria-components';
import { FocusScope } from 'react-aria';
@@ -20,12 +19,14 @@ import { FocusScope } from 'react-aria';
* @param {string} [props.className] - Classes to pass to the container.
* @param {string} [props.contentClassName] - Classes to pass to the inner content wrapper.
* @param {string} [props.labelClassName] - Classes to pass to the label.
+ * @param {string} [props.headerClassName] - Classes to pass to the header (label + trigger).
* @param {JSX.Element|JSX.Element[]} [props.actions] - Actions to display in the panel header, left of the expand button.
* @param {boolean} [props.keepActionsOnExpand=false] - If `true`, the actions are not hidden when the panel is expanded.
* @param {boolean} [props.disabled] - If `true`, the expand button is disabled.
* @param {boolean} [props.noFocusHandling] - If `true`, the focus trapping when the item is expanded is disabled. Useful when part of another component that manages focus itself.
* @param {boolean} [props.open] - Whether the expandable is open.
* @param {Function} [props.onOpenChange] - Function is called when the panel is opened or closed.
+ * @param {object} [props.headerProps] - Props to pass to the header (label + trigger).
* @param {boolean} [props.hidden] - If `true`, the component is not rendered.
*
* @returns {JSX.Element} The Expandable component.
@@ -46,6 +47,7 @@ export const Expandable = (props) => {
className,
labelClassName,
contentClassName,
+ headerClassName,
actions,
@@ -60,6 +62,10 @@ export const Expandable = (props) => {
open = false,
onOpenChange,
+ customOpenButton,
+
+ headerProps,
+
hidden,
...other
@@ -84,7 +90,10 @@ export const Expandable = (props) => {
)}
{...other}
>
-
+
{
{actions && keepActionsOnExpand && {actions}
}
- {
- setIsOpen(!isOpen);
-
- if (onOpenChange) {
- onOpenChange(!isOpen);
- }
- }}
- tooltip={isOpen ? __('Close', 'eightshift-ui-components') : __('Open', 'eightshift-ui-components')}
- disabled={disabled}
- className={clsx('[&>svg]:es-uic-size-5 [&>svg]:es-uic-transition-transform', isOpen && '[&>svg]:-es-uic-scale-y-100')}
- size='small'
- />
+ {customOpenButton &&
+ customOpenButton({
+ open: isOpen,
+ toggleOpen: () => {
+ setIsOpen(!isOpen);
+
+ if (onOpenChange) {
+ onOpenChange(!isOpen);
+ }
+ },
+ tooltip: isOpen ? __('Close', 'eightshift-ui-components') : __('Open', 'eightshift-ui-components'),
+ disabled,
+ })}
+
+ {!customOpenButton && (
+ {
+ setIsOpen(!isOpen);
+
+ if (onOpenChange) {
+ onOpenChange(!isOpen);
+ }
+ }}
+ tooltip={isOpen ? __('Close', 'eightshift-ui-components') : __('Open', 'eightshift-ui-components')}
+ disabled={disabled}
+ className={clsx('[&>svg]:es-uic-size-5 [&>svg]:es-uic-transition-transform', isOpen && '[&>svg]:-es-uic-scale-y-100')}
+ size='small'
+ />
+ )}
{
const { children, className, title, icon, subtitle, help } = props;
return (
-
+
{title && (
{
);
};
+/**
+ * Component that provides a container for grouping related options within the `OptionsPanel` component.
+ *
+ * @component
+ * @param {Object} props - Component props.
+ * @param {string} [props.className] - Classes to pass to the container.
+ *
+ * @returns {JSX.Element} The OptionsPanelSection component.
+ *
+ * @example
+ *
+ * ...
+ *
+ *
+ * @preserve
+ */
export const OptionsPanelSection = ({ children, className }) => {
return (
{
);
};
+
+/**
+ * Component that provides a header for the top of an options page.
+ *
+ * @component
+ * @param {Object} props - Component props.
+ * @param {boolean} [props.sticky] - If `true`, the header will be sticky (scroll with content). Make sure to pass a background color!
+ * @param {string} [props.title] - Title to show.
+ * @param {Number} [props.level=2] - Heading level of the title.
+ * @param {string} [props.className] - Classes to pass to the container.
+ * @param {JSX.Element|JSX.Element[]} [props.actions] - Controls to show on the right side of the header.
+ *
+ * @returns {JSX.Element} The OptionsPanelHeader component.
+ *
+ * @example
+ *
+ * ...
+ *
+ *
+ * @preserve
+ */
+export const OptionsPanelHeader = ({ children, sticky, title, className, actions, level = 2 }) => {
+ return (
+
+
+
+ {title}
+
+
{actions}
+
+ {children}
+
+ );
+};
diff --git a/lib/components/repeater/repeater-item.jsx b/lib/components/repeater/repeater-item.jsx
index 3ca6052..e8d0e26 100644
--- a/lib/components/repeater/repeater-item.jsx
+++ b/lib/components/repeater/repeater-item.jsx
@@ -1,12 +1,11 @@
-import { GridListItem, GridListContext } from 'react-aria-components';
import { Button } from '../button/button';
+import { Menu, MenuItem, MenuSeparator } from '../menu/menu';
import { icons } from '../../icons/icons';
import { clsx } from 'clsx/lite';
import { useContext } from 'react';
import { Expandable } from '../expandable/expandable';
import { __ } from '@wordpress/i18n';
import { RepeaterContext } from './repeater-context';
-import { useRef } from 'react';
/**
* A Repeater item.
@@ -19,6 +18,8 @@ import { useRef } from 'react';
* @param {JSX.Element|JSX.Element[]} [props.actions] - Actions to display to the right of the label.
* @param {string} [props.textValue] - The text value of the item.
* @param {string} [props.className] - Classes to pass to the item.
+ * @param {JSX.Element|JSX.Element[]} [props.menuOptions] - Additional menu options to display next to the expand button.
+ * @param {bool} [props.noMenuButton] - If `true`, the menu button next to the expand button is not displayed.
* @param {bool} [props.expandDisabled] - If `true`, the item cannot be expanded.
*
* @returns {JSX.Element} The RepeaterItem component.
@@ -28,39 +29,78 @@ import { useRef } from 'react';
* @preserve
*/
export const RepeaterItem = (props) => {
- const { children, icon, label, subtitle, 'aria-label': ariaLabel, className, actions, textValue, expandDisabled, ...rest } = props;
+ const { children, icon, label, subtitle, 'aria-label': ariaLabel, className, actions, textValue, expandDisabled, menuOptions, noMenuButton, ...rest } = props;
- const { canDelete, handleOpenChange, deleteItem, isDragSource } = useContext(RepeaterContext);
+ const { deleteItem, duplicateItem, isDragged, isOutOfBounds, isSelected, canDelete, canAdd } = useContext(RepeaterContext);
return (
- <>
- handleOpenChange(isOpen)}
- actions={
- <>
- {actions}
+ {
+ return (
+
+
svg]:es-uic-size-5 [&>svg]:es-uic-shrink-0',
+ size: 'small',
+ type: 'ghost',
+ }}
+ tooltip={__('More options', 'eightshift-ui-components')}
+ >
+ {menuOptions}
+
+ {menuOptions && }
+
+ duplicateItem()}
+ >
+ {__('Duplicate', 'eightshift-ui-components')}
+
+
+ deleteItem()}
+ >
+ {__('Remove', 'eightshift-ui-components')}
+
+
deleteItem()}
- className='es-uic-translate-x-px'
+ icon={open ? icons.caretDownFill : icons.caretDown}
+ onPress={toggleOpen}
+ tooltip={tooltip}
+ disabled={disabled}
+ className={clsx('[&>svg]:es-uic-size-5 [&>svg]:es-uic-transition-transform', open && '[&>svg]:-es-uic-scale-y-100')}
+ size='small'
/>
- >
- }
- noFocusHandling
- >
- {children}
-
- >
+
+ );
+ }}
+ actions={actions}
+ headerProps={{ 'data-movable-handle': true }}
+ noFocusHandling
+ {...rest}
+ >
+ {children}
+
);
};
diff --git a/lib/components/repeater/repeater.jsx b/lib/components/repeater/repeater.jsx
index 41e3b4d..8e230fb 100644
--- a/lib/components/repeater/repeater.jsx
+++ b/lib/components/repeater/repeater.jsx
@@ -1,33 +1,18 @@
import { __ } from '@wordpress/i18n';
import { Button } from '../button/button';
import { icons } from '../../icons/icons';
-import { useId, useState, useEffect, useRef } from 'react';
+import { useId } from 'react';
import { BaseControl } from '../base-control/base-control';
import { AnimatedVisibility } from '../animated-visibility/animated-visibility';
-import { ToggleButton } from '../toggle-button/toggle-button';
-import { useSortable } from '@dnd-kit/react/sortable';
import { RepeaterContext } from './repeater-context';
-import { RestrictToVerticalAxis } from '@dnd-kit/abstract/modifiers';
-import { move } from '@dnd-kit/helpers';
-import { DragDropProvider } from '@dnd-kit/react';
import { clsx } from 'clsx/lite';
+import { List, arrayMove, arrayRemove } from 'react-movable';
-const SortableItem = ({ id, index, disabled, children }) => {
- const [element, setElement] = useState(null);
- const handleRef = useRef(null);
-
- const { isDragSource, status } = useSortable({
- id,
- index,
- element,
- type: 'item',
- modifiers: [RestrictToVerticalAxis],
- transition: { idle: true, duration: 400, easing: 'cubic-bezier(0, 0.55, 0.45, 1)' }, // 'easeOutCirc'
- handle: handleRef,
- disabled,
- });
-
- return {children(handleRef, isDragSource, status)}
;
+const fixIds = (items, itemIdBase) => {
+ return items.map((item, i) => ({
+ ...item,
+ id: item?.id ?? `${itemIdBase}-${i}`,
+ }));
};
/**
@@ -48,6 +33,7 @@ const SortableItem = ({ id, index, disabled, children }) => {
* @param {Function} [props.onAfterItemAdd] - Function to run after an item is added.
* @param {Function} [props.onAfterItemRemove] - Function to run after an item is removed.
* @param {Number} [props.minItems] - The minimum number of items that must be present. If there are less items than this, deleting items will be disabled.
+ * @param {Number} [props.maxItems] - The maximum number of items that can be present. If there are more items than this, adding items will be disabled.
* @param {boolean} [props.hidden] - If `true`, the component is not rendered.
* @param {JSX.Element} [props.addButton] - If provided, overrides the default add button. `(props: { addItem: (additional: Object?) => void, disabled: Boolean }) => JSX.Element`.
* @param {string} [props.className] - Classes to pass to the item wrapper.
@@ -100,6 +86,7 @@ export const Repeater = (props) => {
onAfterItemAdd,
onAfterItemRemove,
minItems,
+ maxItems,
addButton,
className,
emptyState,
@@ -107,29 +94,10 @@ export const Repeater = (props) => {
hidden,
} = props;
- const fixIds = (items) => {
- return items.map((item, i) => ({
- ...item,
- id: item?.id ?? `${itemIdBase}-${i}`,
- }));
- };
-
- const [items, setItems] = useState(fixIds(rawItems));
-
- const [canDelete, setCanDelete] = useState(false);
- const [isPanelOpen, setIsPanelOpen] = useState({});
- const [isDragging, setIsDragging] = useState(false);
+ const items = fixIds(rawItems, itemIdBase);
- const isAnyPanelOpen = Object.keys(isPanelOpen)?.length < 1 ? false : Object.entries(isPanelOpen).some(([_, v]) => v === true);
-
- if (canDelete && items.length < (minItems ?? 1)) {
- setCanDelete(false);
- }
-
- // Ensure the internal state is updated if items are updated externally.
- useEffect(() => {
- setItems(fixIds(rawItems));
- }, [rawItems]);
+ const canDelete = items.length > (minItems ?? 0);
+ const canAdd = items.length < (maxItems ?? Number.MAX_SAFE_INTEGER);
if (hidden) {
return null;
@@ -145,17 +113,6 @@ export const Repeater = (props) => {
<>
{actions}
- 0}>
-
-
-
{!addButton && (
{
@@ -170,7 +127,7 @@ export const Repeater = (props) => {
icon={icons.add}
className={clsx('[&>svg]:es-uic-size-4', !hideEmptyState && items.length < 1 && 'es-uic-invisible')}
tooltip={__('Add item', 'eightshift-ui-components')}
- disabled={addDisabled || canDelete}
+ disabled={addDisabled || !canAdd}
/>
)}
@@ -185,7 +142,7 @@ export const Repeater = (props) => {
onAfterItemAdd(newItem);
}
},
- disabled: addDisabled || canDelete,
+ disabled: addDisabled,
})}
)}
@@ -193,123 +150,79 @@ export const Repeater = (props) => {
}
className='es-uic-w-full'
>
-
-
{
- if (isAnyPanelOpen) {
- event.preventDefault();
-
- return;
- }
-
- setIsDragging(true);
- }}
- onDragOver={(event) => {
- if (isAnyPanelOpen) {
- event.preventDefault();
-
- return;
- }
-
- const { source, target } = event.operation;
-
- if (!source || !target) {
- return;
- }
-
- setItems((items) => move(items, source, target));
- }}
- onDragEnd={(event) => {
- if (isAnyPanelOpen) {
- event.preventDefault();
-
- return;
- }
-
- const { source, target } = event.operation;
-
- if (!source || !target) {
- return;
- }
-
- if (event.canceled) {
- return;
- }
-
- setIsDragging(false);
-
- setItems((items) => move(items, source, target));
- onChange(items);
- }}
- >
- {items.map((item, index) => (
- onChange(newIndex === -1 ? arrayRemove(items, oldIndex) : arrayMove(items, oldIndex, newIndex))}
+ renderList={({ children, props }) => {
+ const { key, ...rest } = props;
+
+ return (
+
- {(handleRef, isDragSource, status) => {
- return (
- {
- setItems([...items].filter((i) => i.id !== item.id));
- onChange([...items].filter((i) => i.id !== item.id));
-
- if (onAfterItemRemove) {
- onAfterItemRemove(item);
- }
- },
- handleOpenChange: (isOpen) => setIsPanelOpen((data) => ({ ...data, [item.id]: isOpen })),
- isDragSource,
- }}
- >
-
-
-
- {children({
- ...item,
- updateData: (newValue) => {
- const updated = [...items].map((i) => (i.id === item.id ? { ...i, ...newValue } : i));
-
- onChange(updated);
- setItems(updated);
- },
- itemIndex: index,
- deleteItem: () => {
- setItems([...items].filter((i) => i.id !== item.id));
- onChange([...items].filter((i) => i.id !== item.id));
-
- if (onAfterItemRemove) {
- onAfterItemRemove(item);
- }
- },
- })}
-
-
- );
- }}
-
- ))}
-
-
+ {children}
+
+ );
+ }}
+ renderItem={({ value: item, index, isDragged, isSelected, isOutOfBounds, props }) => {
+ const { key, ...rest } = props;
+
+ return (
+
+ {
+ onChange([...items].filter((i) => i.id !== item.id));
+
+ if (onAfterItemRemove) {
+ onAfterItemRemove(item);
+ }
+ },
+ duplicateItem: () => {
+ const newItem = { ...item, id: `${itemIdBase}${items.length + 1}` };
+ onChange([...items, newItem]);
+
+ if (onAfterItemAdd) {
+ onAfterItemAdd(newItem);
+ }
+ },
+ isDragged,
+ isOutOfBounds,
+ isSelected,
+ canDelete,
+ canAdd,
+ }}
+ >
+ {children({
+ ...item,
+ updateData: (newValue) => {
+ const updated = [...items].map((i) => (i.id === item.id ? { ...i, ...newValue } : i));
+
+ onChange(updated);
+ },
+ itemIndex: index,
+ deleteItem: () => {
+ onChange([...items].filter((i) => i.id !== item.id));
+
+ if (onAfterItemRemove) {
+ onAfterItemRemove(item);
+ }
+ },
+ })}
+
+
+ );
+ }}
+ removableByMove
+ />
{emptyState}
@@ -346,7 +259,7 @@ export const Repeater = (props) => {
onAfterItemAdd(newItem);
}
},
- disabled: addDisabled || canDelete,
+ disabled: addDisabled,
})}
)}
diff --git a/package.json b/package.json
index 6fb4b94..945a2ea 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@eightshift/ui-components",
- "version": "1.5.1",
+ "version": "1.6.0",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -49,9 +49,9 @@
"@dnd-kit/react": "^0.0.5",
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "react-jsx-parser": "^2.0.0",
- "svg-to-jsx-string": "^0.1.1",
- "swapy": "^0.2.1"
+ "react-jsx-parser": "^2.1.0",
+ "react-movable": "^3.3.1",
+ "svg-to-jsx-string": "^0.1.1"
},
"devDependencies": {
"@adobe/react-spectrum": "^3.36.3",
@@ -62,18 +62,18 @@
"@eslint/compat": "^1.1.1",
"@react-stately/collections": "^3.10.9",
"@stylistic/eslint-plugin-js": "^2.8.0",
- "@types/react": "^18.3.3",
+ "@types/react": "^18.3.9",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react-swc": "^3.7.0",
- "@wordpress/i18n": "^5.7.0",
+ "@wordpress/i18n": "^5.8.0",
"autoprefixer": "^10.4.20",
"clsx": "^2.1.1",
"css-gradient-parser": "^0.0.16",
- "eslint": "^9.10.0",
+ "eslint": "^9.11.1",
"eslint-config-prettier": "^9.1.0",
- "eslint-plugin-jsdoc": "^50.2.2",
+ "eslint-plugin-jsdoc": "^50.3.0",
"eslint-plugin-prettier": "^5.2.1",
- "framer-motion": "^11.5.4",
+ "framer-motion": "^11.8.0",
"glob": "^11.0.0",
"globals": "^15.9.0",
"just-camel-case": "^6.2.0",
@@ -83,18 +83,18 @@
"just-kebab-case": "^4.2.0",
"just-throttle": "^4.2.0",
"lightningcss": "^1.27.0",
- "postcss": "^8.4.45",
+ "postcss": "^8.4.47",
"prettier": "^3.3.3",
- "prettier-plugin-tailwindcss": "^0.6.6",
+ "prettier-plugin-tailwindcss": "^0.6.8",
"react-aria": "^3.34.3",
"react-aria-components": "^1.3.3",
- "react-select": "^5.8.0",
+ "react-select": "^5.8.1",
"react-stately": "^3.32.2",
- "tailwindcss": "^3.4.10",
+ "tailwindcss": "^3.4.13",
"tailwindcss-animate": "^1.0.7",
"tailwindcss-react-aria-components": "^1.1.5",
"tailwindcss-scoped-preflight": "^3.4.4",
- "vite": "^5.4.4",
+ "vite": "^5.4.8",
"vite-plugin-lib-inject-css": "^2.1.1"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 73d945b..3acfe37 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -27,14 +27,14 @@ importers:
specifier: ^18.3.1
version: 18.3.1(react@18.3.1)
react-jsx-parser:
- specifier: ^2.0.0
- version: 2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ specifier: ^2.1.0
+ version: 2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react-movable:
+ specifier: ^3.3.1
+ version: 3.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
svg-to-jsx-string:
specifier: ^0.1.1
version: 0.1.1
- swapy:
- specifier: ^0.2.1
- version: 0.2.1
devDependencies:
'@adobe/react-spectrum':
specifier: ^3.36.3
@@ -59,22 +59,22 @@ importers:
version: 3.10.9(react@18.3.1)
'@stylistic/eslint-plugin-js':
specifier: ^2.8.0
- version: 2.8.0(eslint@9.10.0(jiti@1.21.6))
+ version: 2.8.0(eslint@9.11.1(jiti@1.21.6))
'@types/react':
- specifier: ^18.3.3
- version: 18.3.5
+ specifier: ^18.3.9
+ version: 18.3.9
'@types/react-dom':
specifier: ^18.3.0
version: 18.3.0
'@vitejs/plugin-react-swc':
specifier: ^3.7.0
- version: 3.7.0(@swc/helpers@0.5.13)(vite@5.4.4(lightningcss@1.27.0))
+ version: 3.7.0(@swc/helpers@0.5.13)(vite@5.4.8(lightningcss@1.27.0))
'@wordpress/i18n':
- specifier: ^5.7.0
- version: 5.7.0
+ specifier: ^5.8.0
+ version: 5.8.0
autoprefixer:
specifier: ^10.4.20
- version: 10.4.20(postcss@8.4.45)
+ version: 10.4.20(postcss@8.4.47)
clsx:
specifier: ^2.1.1
version: 2.1.1
@@ -82,20 +82,20 @@ importers:
specifier: ^0.0.16
version: 0.0.16
eslint:
- specifier: ^9.10.0
- version: 9.10.0(jiti@1.21.6)
+ specifier: ^9.11.1
+ version: 9.11.1(jiti@1.21.6)
eslint-config-prettier:
specifier: ^9.1.0
- version: 9.1.0(eslint@9.10.0(jiti@1.21.6))
+ version: 9.1.0(eslint@9.11.1(jiti@1.21.6))
eslint-plugin-jsdoc:
- specifier: ^50.2.2
- version: 50.2.2(eslint@9.10.0(jiti@1.21.6))
+ specifier: ^50.3.0
+ version: 50.3.0(eslint@9.11.1(jiti@1.21.6))
eslint-plugin-prettier:
specifier: ^5.2.1
- version: 5.2.1(eslint-config-prettier@9.1.0(eslint@9.10.0(jiti@1.21.6)))(eslint@9.10.0(jiti@1.21.6))(prettier@3.3.3)
+ version: 5.2.1(eslint-config-prettier@9.1.0(eslint@9.11.1(jiti@1.21.6)))(eslint@9.11.1(jiti@1.21.6))(prettier@3.3.3)
framer-motion:
- specifier: ^11.5.4
- version: 11.5.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ specifier: ^11.8.0
+ version: 11.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
glob:
specifier: ^11.0.0
version: 11.0.0
@@ -124,14 +124,14 @@ importers:
specifier: ^1.27.0
version: 1.27.0
postcss:
- specifier: ^8.4.45
- version: 8.4.45
+ specifier: ^8.4.47
+ version: 8.4.47
prettier:
specifier: ^3.3.3
version: 3.3.3
prettier-plugin-tailwindcss:
- specifier: ^0.6.6
- version: 0.6.6(prettier@3.3.3)
+ specifier: ^0.6.8
+ version: 0.6.8(prettier@3.3.3)
react-aria:
specifier: ^3.34.3
version: 3.34.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -139,29 +139,29 @@ importers:
specifier: ^1.3.3
version: 1.3.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-select:
- specifier: ^5.8.0
- version: 5.8.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ specifier: ^5.8.1
+ version: 5.8.1(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-stately:
specifier: ^3.32.2
version: 3.32.2(react@18.3.1)
tailwindcss:
- specifier: ^3.4.10
- version: 3.4.10
+ specifier: ^3.4.13
+ version: 3.4.13
tailwindcss-animate:
specifier: ^1.0.7
- version: 1.0.7(tailwindcss@3.4.10)
+ version: 1.0.7(tailwindcss@3.4.13)
tailwindcss-react-aria-components:
specifier: ^1.1.5
- version: 1.1.5(tailwindcss@3.4.10)
+ version: 1.1.5(tailwindcss@3.4.13)
tailwindcss-scoped-preflight:
specifier: ^3.4.4
- version: 3.4.4(postcss@8.4.45)(tailwindcss@3.4.10)
+ version: 3.4.4(postcss@8.4.47)(tailwindcss@3.4.13)
vite:
- specifier: ^5.4.4
- version: 5.4.4(lightningcss@1.27.0)
+ specifier: ^5.4.8
+ version: 5.4.8(lightningcss@1.27.0)
vite-plugin-lib-inject-css:
specifier: ^2.1.1
- version: 2.1.1(vite@5.4.4(lightningcss@1.27.0))
+ version: 2.1.1(vite@5.4.8(lightningcss@1.27.0))
packages:
@@ -537,20 +537,24 @@ packages:
resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ '@eslint/core@0.6.0':
+ resolution: {integrity: sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@eslint/eslintrc@3.1.0':
resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/js@9.10.0':
- resolution: {integrity: sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==}
+ '@eslint/js@9.11.1':
+ resolution: {integrity: sha512-/qu+TWz8WwPWc7/HcIJKi+c+MOm46GdVaSlTTQcaqaL53+GsoA6MxWp5PtTx48qbSP7ylM1Kn7nhvkugfJvRSA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/object-schema@2.1.4':
resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/plugin-kit@0.1.0':
- resolution: {integrity: sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==}
+ '@eslint/plugin-kit@0.2.0':
+ resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@floating-ui/core@1.6.7':
@@ -1762,6 +1766,12 @@ packages:
'@types/estree@1.0.5':
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
+ '@types/estree@1.0.6':
+ resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
'@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
@@ -1774,20 +1784,20 @@ packages:
'@types/react-transition-group@4.4.11':
resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==}
- '@types/react@18.3.5':
- resolution: {integrity: sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==}
+ '@types/react@18.3.9':
+ resolution: {integrity: sha512-+BpAVyTpJkNWWSSnaLBk6ePpHLOGJKnEQNbINNovPWzvEUyAe3e+/d494QdEh71RekM/qV7lw6jzf1HGrJyAtQ==}
'@vitejs/plugin-react-swc@3.7.0':
resolution: {integrity: sha512-yrknSb3Dci6svCd/qhHqhFPDSw0QtjumcqdKMoNNzmOl5lMXTTiqzjWtG4Qask2HdvvzaNgSunbQGet8/GrKdA==}
peerDependencies:
vite: ^4 || ^5
- '@wordpress/hooks@4.7.0':
- resolution: {integrity: sha512-EGHMsNCt+PyStm3o1JWujaTA+HKcTxuEXdSHBBFDavzsgOF13bxTf1LpDYgTZJT3K9TSMP983IwfckP5t66pDw==}
+ '@wordpress/hooks@4.8.0':
+ resolution: {integrity: sha512-6CPXtkZOHg8Q9gFulbuB+V74yCaPK2E2nRMw2BXE1yNfIAItqMbUiC8zrNOamtLcg3ifsk1PPeJ2DX5mR7Wyug==}
engines: {node: '>=18.12.0', npm: '>=8.19.2'}
- '@wordpress/i18n@5.7.0':
- resolution: {integrity: sha512-o1cq1zutE5rMAM//Ra1hRfgHuWNBxFtd7XNk+BuAcILRENMaEoqAoGBmGj9lRtOcqAj+cdoWxFjBIxRa67vIrg==}
+ '@wordpress/i18n@5.8.0':
+ resolution: {integrity: sha512-pPx8RPT69Kds8wygHGfkt+D2jxdyu2HIYw3yM+dj47rNW2rHtZFVoOr+QzwOJ4yoHRuN1zMhOfzHsC4WV+ARcg==}
engines: {node: '>=18.12.0', npm: '>=8.19.2'}
hasBin: true
@@ -1931,9 +1941,6 @@ packages:
convert-source-map@1.9.0:
resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
- core-js@3.38.1:
- resolution: {integrity: sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==}
-
cosmiconfig@7.1.0:
resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==}
engines: {node: '>=10'}
@@ -2024,8 +2031,8 @@ packages:
peerDependencies:
eslint: '>=7.0.0'
- eslint-plugin-jsdoc@50.2.2:
- resolution: {integrity: sha512-i0ZMWA199DG7sjxlzXn5AeYZxpRfMJjDPUl7lL9eJJX8TPRoIaxJU4ys/joP5faM5AXE1eqW/dslCj3uj4Nqpg==}
+ eslint-plugin-jsdoc@50.3.0:
+ resolution: {integrity: sha512-P7qDB/RckdKETpBM4CtjHRQ5qXByPmFhRi86sN3E+J+tySchq+RSOGGhI2hDIefmmKFuTi/1ACjqsnDJDDDfzg==}
engines: {node: '>=18'}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
@@ -2056,8 +2063,8 @@ packages:
resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- eslint@9.10.0:
- resolution: {integrity: sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==}
+ eslint@9.11.1:
+ resolution: {integrity: sha512-MobhYKIoAO1s1e4VUrgx1l1Sk2JBR/Gqjjgw8+mfgoLE2xwsHur4gdfTxyTgShrhvdVFTaJSgMiQBl1jv/AWxg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
peerDependencies:
@@ -2134,8 +2141,8 @@ packages:
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
- framer-motion@11.5.4:
- resolution: {integrity: sha512-E+tb3/G6SO69POkdJT+3EpdMuhmtCh9EWuK4I1DnIC23L7tFPrl8vxP+LSovwaw6uUr73rUbpb4FgK011wbRJQ==}
+ framer-motion@11.8.0:
+ resolution: {integrity: sha512-q/axN/PFRdKmzPK6PO2OhbLUMWXXZuiejdM1/3FhC2hm4YIetc+qeco2EvWm4u1/UTFmevclE492wGFNfSZ4eQ==}
peerDependencies:
'@emotion/is-prop-valid': '*'
react: ^18.0.0
@@ -2580,8 +2587,8 @@ packages:
postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
- postcss@8.4.45:
- resolution: {integrity: sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==}
+ postcss@8.4.47:
+ resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==}
engines: {node: ^10 || ^12 || >=14}
prelude-ls@1.2.1:
@@ -2592,8 +2599,8 @@ packages:
resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
engines: {node: '>=6.0.0'}
- prettier-plugin-tailwindcss@0.6.6:
- resolution: {integrity: sha512-OPva5S7WAsPLEsOuOWXATi13QrCKACCiIonFgIR6V4lYv4QLp++UXVhZSzRbZxXGimkQtQT86CC6fQqTOybGng==}
+ prettier-plugin-tailwindcss@0.6.8:
+ resolution: {integrity: sha512-dGu3kdm7SXPkiW4nzeWKCl3uoImdd5CTZEJGxyypEPL37Wj0HT2pLqjrvSei1nTeuQfO4PUfjeW5cTUNRLZ4sA==}
engines: {node: '>=14.21.3'}
peerDependencies:
'@ianvs/prettier-plugin-sort-imports': '*'
@@ -2682,14 +2689,21 @@ packages:
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
- react-jsx-parser@2.0.0:
- resolution: {integrity: sha512-1siP+vM47gWE+x2VTF0ano5jJPRilOf7sRIoy4dzb1FmNoDpwe3htuzgcEfvzsc7YThzwmMQNUfy4vAv1ATs2g==}
+ react-jsx-parser@2.1.0:
+ resolution: {integrity: sha512-cGp8ceqA6j7OylsqeK2kuCGRitHpWzximsxsQyqkQO+1fwyG82Mb1l2nx9ImrfdCO6GT2kgt7C6cX9vJ46B7ow==}
+ engines: {bun: ^1.1.27}
peerDependencies:
react: '>=18'
react-dom: '>=18'
- react-select@5.8.0:
- resolution: {integrity: sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==}
+ react-movable@3.3.1:
+ resolution: {integrity: sha512-dfX15ZlyoefoFs1kqVeLrJoso4eaTEBwP2pyUanDUyNg1Uy+lzxOv0GyNcmHJTZUtLSaNbim8DdekpKALSLKkA==}
+ peerDependencies:
+ react: '*'
+ react-dom: '*'
+
+ react-select@5.8.1:
+ resolution: {integrity: sha512-RT1CJmuc+ejqm5MPgzyZujqDskdvB9a9ZqrdnVLsvAHjJ3Tj0hELnLeVPQlmYdVKCdCpxanepl6z7R5KhXhWzg==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -2831,9 +2845,6 @@ packages:
svg-to-jsx-string@0.1.1:
resolution: {integrity: sha512-iZ6vpKQkZxX2TPnlWmb/GuSV9Msjfp7YVgyUnmMOEl9IUw8ilNqNBny/T/2tt6ctV1EqaDvOs5f7X0LrTIkqtA==}
- swapy@0.2.1:
- resolution: {integrity: sha512-11pu6YJMXtmJLXPeocyAtfaPZkOeby1mYdZNmhvvek9E8rMwfDbFk4Lwlbd+XaxBCSf3vMZwJo8DsNOKWkXmLA==}
-
synckit@0.9.1:
resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -2854,8 +2865,8 @@ packages:
postcss: ^8
tailwindcss: ^3
- tailwindcss@3.4.10:
- resolution: {integrity: sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==}
+ tailwindcss@3.4.13:
+ resolution: {integrity: sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -2921,8 +2932,8 @@ packages:
peerDependencies:
vite: '*'
- vite@5.4.4:
- resolution: {integrity: sha512-RHFCkULitycHVTtelJ6jQLd+KSAAzOgEYorV32R2q++M6COBjKJR6BxqClwp5sf0XaBDjVMuJ9wnNfyAJwjMkA==}
+ vite@5.4.8:
+ resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -3261,7 +3272,7 @@ snapshots:
'@emotion/memoize@0.9.0': {}
- '@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1)':
+ '@emotion/react@11.13.3(@types/react@18.3.9)(react@18.3.1)':
dependencies:
'@babel/runtime': 7.25.6
'@emotion/babel-plugin': 11.12.0
@@ -3273,7 +3284,7 @@ snapshots:
hoist-non-react-statics: 3.3.2
react: 18.3.1
optionalDependencies:
- '@types/react': 18.3.5
+ '@types/react': 18.3.9
transitivePeerDependencies:
- supports-color
@@ -3372,9 +3383,9 @@ snapshots:
'@esbuild/win32-x64@0.21.5':
optional: true
- '@eslint-community/eslint-utils@4.4.0(eslint@9.10.0(jiti@1.21.6))':
+ '@eslint-community/eslint-utils@4.4.0(eslint@9.11.1(jiti@1.21.6))':
dependencies:
- eslint: 9.10.0(jiti@1.21.6)
+ eslint: 9.11.1(jiti@1.21.6)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.11.0': {}
@@ -3389,6 +3400,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@eslint/core@0.6.0': {}
+
'@eslint/eslintrc@3.1.0':
dependencies:
ajv: 6.12.6
@@ -3403,11 +3416,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@eslint/js@9.10.0': {}
+ '@eslint/js@9.11.1': {}
'@eslint/object-schema@2.1.4': {}
- '@eslint/plugin-kit@0.1.0':
+ '@eslint/plugin-kit@0.2.0':
dependencies:
levn: 0.4.1
@@ -5441,9 +5454,9 @@ snapshots:
transitivePeerDependencies:
- react-dom
- '@stylistic/eslint-plugin-js@2.8.0(eslint@9.10.0(jiti@1.21.6))':
+ '@stylistic/eslint-plugin-js@2.8.0(eslint@9.11.1(jiti@1.21.6))':
dependencies:
- eslint: 9.10.0(jiti@1.21.6)
+ eslint: 9.11.1(jiti@1.21.6)
eslint-visitor-keys: 4.0.0
espree: 10.1.0
@@ -5519,38 +5532,42 @@ snapshots:
'@types/estree@1.0.5': {}
+ '@types/estree@1.0.6': {}
+
+ '@types/json-schema@7.0.15': {}
+
'@types/parse-json@4.0.2': {}
'@types/prop-types@15.7.12': {}
'@types/react-dom@18.3.0':
dependencies:
- '@types/react': 18.3.5
+ '@types/react': 18.3.9
'@types/react-transition-group@4.4.11':
dependencies:
- '@types/react': 18.3.5
+ '@types/react': 18.3.9
- '@types/react@18.3.5':
+ '@types/react@18.3.9':
dependencies:
'@types/prop-types': 15.7.12
csstype: 3.1.3
- '@vitejs/plugin-react-swc@3.7.0(@swc/helpers@0.5.13)(vite@5.4.4(lightningcss@1.27.0))':
+ '@vitejs/plugin-react-swc@3.7.0(@swc/helpers@0.5.13)(vite@5.4.8(lightningcss@1.27.0))':
dependencies:
'@swc/core': 1.7.26(@swc/helpers@0.5.13)
- vite: 5.4.4(lightningcss@1.27.0)
+ vite: 5.4.8(lightningcss@1.27.0)
transitivePeerDependencies:
- '@swc/helpers'
- '@wordpress/hooks@4.7.0':
+ '@wordpress/hooks@4.8.0':
dependencies:
'@babel/runtime': 7.25.6
- '@wordpress/i18n@5.7.0':
+ '@wordpress/i18n@5.8.0':
dependencies:
'@babel/runtime': 7.25.6
- '@wordpress/hooks': 4.7.0
+ '@wordpress/hooks': 4.8.0
gettext-parser: 1.4.0
memize: 2.1.0
sprintf-js: 1.1.3
@@ -5596,14 +5613,14 @@ snapshots:
argparse@2.0.1: {}
- autoprefixer@10.4.20(postcss@8.4.45):
+ autoprefixer@10.4.20(postcss@8.4.47):
dependencies:
browserslist: 4.23.3
caniuse-lite: 1.0.30001660
fraction.js: 4.3.7
normalize-range: 0.1.2
picocolors: 1.1.0
- postcss: 8.4.45
+ postcss: 8.4.47
postcss-value-parser: 4.2.0
babel-plugin-macros@3.1.0:
@@ -5689,8 +5706,6 @@ snapshots:
convert-source-map@1.9.0: {}
- core-js@3.38.1: {}
-
cosmiconfig@7.1.0:
dependencies:
'@types/parse-json': 4.0.2
@@ -5778,18 +5793,18 @@ snapshots:
escape-string-regexp@4.0.0: {}
- eslint-config-prettier@9.1.0(eslint@9.10.0(jiti@1.21.6)):
+ eslint-config-prettier@9.1.0(eslint@9.11.1(jiti@1.21.6)):
dependencies:
- eslint: 9.10.0(jiti@1.21.6)
+ eslint: 9.11.1(jiti@1.21.6)
- eslint-plugin-jsdoc@50.2.2(eslint@9.10.0(jiti@1.21.6)):
+ eslint-plugin-jsdoc@50.3.0(eslint@9.11.1(jiti@1.21.6)):
dependencies:
'@es-joy/jsdoccomment': 0.48.0
are-docs-informative: 0.0.2
comment-parser: 1.4.1
debug: 4.3.7
escape-string-regexp: 4.0.0
- eslint: 9.10.0(jiti@1.21.6)
+ eslint: 9.11.1(jiti@1.21.6)
espree: 10.1.0
esquery: 1.6.0
parse-imports: 2.1.1
@@ -5799,14 +5814,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0(eslint@9.10.0(jiti@1.21.6)))(eslint@9.10.0(jiti@1.21.6))(prettier@3.3.3):
+ eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0(eslint@9.11.1(jiti@1.21.6)))(eslint@9.11.1(jiti@1.21.6))(prettier@3.3.3):
dependencies:
- eslint: 9.10.0(jiti@1.21.6)
+ eslint: 9.11.1(jiti@1.21.6)
prettier: 3.3.3
prettier-linter-helpers: 1.0.0
synckit: 0.9.1
optionalDependencies:
- eslint-config-prettier: 9.1.0(eslint@9.10.0(jiti@1.21.6))
+ eslint-config-prettier: 9.1.0(eslint@9.11.1(jiti@1.21.6))
eslint-scope@8.0.2:
dependencies:
@@ -5817,17 +5832,20 @@ snapshots:
eslint-visitor-keys@4.0.0: {}
- eslint@9.10.0(jiti@1.21.6):
+ eslint@9.11.1(jiti@1.21.6):
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6))
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.11.1(jiti@1.21.6))
'@eslint-community/regexpp': 4.11.0
'@eslint/config-array': 0.18.0
+ '@eslint/core': 0.6.0
'@eslint/eslintrc': 3.1.0
- '@eslint/js': 9.10.0
- '@eslint/plugin-kit': 0.1.0
+ '@eslint/js': 9.11.1
+ '@eslint/plugin-kit': 0.2.0
'@humanwhocodes/module-importer': 1.0.1
'@humanwhocodes/retry': 0.3.0
'@nodelib/fs.walk': 1.2.8
+ '@types/estree': 1.0.6
+ '@types/json-schema': 7.0.15
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
@@ -5925,7 +5943,7 @@ snapshots:
fraction.js@4.3.7: {}
- framer-motion@11.5.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ framer-motion@11.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
tslib: 2.7.0
optionalDependencies:
@@ -6260,28 +6278,28 @@ snapshots:
pirates@4.0.6: {}
- postcss-import@15.1.0(postcss@8.4.45):
+ postcss-import@15.1.0(postcss@8.4.47):
dependencies:
- postcss: 8.4.45
+ postcss: 8.4.47
postcss-value-parser: 4.2.0
read-cache: 1.0.0
resolve: 1.22.8
- postcss-js@4.0.1(postcss@8.4.45):
+ postcss-js@4.0.1(postcss@8.4.47):
dependencies:
camelcase-css: 2.0.1
- postcss: 8.4.45
+ postcss: 8.4.47
- postcss-load-config@4.0.2(postcss@8.4.45):
+ postcss-load-config@4.0.2(postcss@8.4.47):
dependencies:
lilconfig: 3.1.2
yaml: 2.5.1
optionalDependencies:
- postcss: 8.4.45
+ postcss: 8.4.47
- postcss-nested@6.2.0(postcss@8.4.45):
+ postcss-nested@6.2.0(postcss@8.4.47):
dependencies:
- postcss: 8.4.45
+ postcss: 8.4.47
postcss-selector-parser: 6.1.2
postcss-selector-parser@6.1.2:
@@ -6291,7 +6309,7 @@ snapshots:
postcss-value-parser@4.2.0: {}
- postcss@8.4.45:
+ postcss@8.4.47:
dependencies:
nanoid: 3.3.7
picocolors: 1.1.0
@@ -6303,7 +6321,7 @@ snapshots:
dependencies:
fast-diff: 1.3.0
- prettier-plugin-tailwindcss@0.6.6(prettier@3.3.3):
+ prettier-plugin-tailwindcss@0.6.8(prettier@3.3.3):
dependencies:
prettier: 3.3.3
@@ -6402,23 +6420,26 @@ snapshots:
react-is@16.13.1: {}
- react-jsx-parser@2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ react-jsx-parser@2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
acorn: 8.12.1
acorn-jsx: 5.3.2(acorn@8.12.1)
- browserslist: 4.23.3
- core-js: 3.38.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
- '@types/react': 18.3.5
+ '@types/react': 18.3.9
'@types/react-dom': 18.3.0
- react-select@5.8.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ react-movable@3.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+
+ react-select@5.8.1(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@babel/runtime': 7.25.6
'@emotion/cache': 11.13.1
- '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1)
+ '@emotion/react': 11.13.3(@types/react@18.3.9)(react@18.3.1)
'@floating-ui/dom': 1.6.10
'@types/react-transition-group': 4.4.11
memoize-one: 6.0.0
@@ -6426,7 +6447,7 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- use-isomorphic-layout-effect: 1.1.2(@types/react@18.3.5)(react@18.3.1)
+ use-isomorphic-layout-effect: 1.1.2(@types/react@18.3.9)(react@18.3.1)
transitivePeerDependencies:
- '@types/react'
- supports-color
@@ -6598,27 +6619,25 @@ snapshots:
svg-to-jsx-string@0.1.1: {}
- swapy@0.2.1: {}
-
synckit@0.9.1:
dependencies:
'@pkgr/core': 0.1.1
tslib: 2.7.0
- tailwindcss-animate@1.0.7(tailwindcss@3.4.10):
+ tailwindcss-animate@1.0.7(tailwindcss@3.4.13):
dependencies:
- tailwindcss: 3.4.10
+ tailwindcss: 3.4.13
- tailwindcss-react-aria-components@1.1.5(tailwindcss@3.4.10):
+ tailwindcss-react-aria-components@1.1.5(tailwindcss@3.4.13):
dependencies:
- tailwindcss: 3.4.10
+ tailwindcss: 3.4.13
- tailwindcss-scoped-preflight@3.4.4(postcss@8.4.45)(tailwindcss@3.4.10):
+ tailwindcss-scoped-preflight@3.4.4(postcss@8.4.47)(tailwindcss@3.4.13):
dependencies:
- postcss: 8.4.45
- tailwindcss: 3.4.10
+ postcss: 8.4.47
+ tailwindcss: 3.4.13
- tailwindcss@3.4.10:
+ tailwindcss@3.4.13:
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
@@ -6634,11 +6653,11 @@ snapshots:
normalize-path: 3.0.0
object-hash: 3.0.0
picocolors: 1.1.0
- postcss: 8.4.45
- postcss-import: 15.1.0(postcss@8.4.45)
- postcss-js: 4.0.1(postcss@8.4.45)
- postcss-load-config: 4.0.2(postcss@8.4.45)
- postcss-nested: 6.2.0(postcss@8.4.45)
+ postcss: 8.4.47
+ postcss-import: 15.1.0(postcss@8.4.47)
+ postcss-js: 4.0.1(postcss@8.4.47)
+ postcss-load-config: 4.0.2(postcss@8.4.47)
+ postcss-nested: 6.2.0(postcss@8.4.47)
postcss-selector-parser: 6.1.2
resolve: 1.22.8
sucrase: 3.35.0
@@ -6683,11 +6702,11 @@ snapshots:
dependencies:
punycode: 2.3.1
- use-isomorphic-layout-effect@1.1.2(@types/react@18.3.5)(react@18.3.1):
+ use-isomorphic-layout-effect@1.1.2(@types/react@18.3.9)(react@18.3.1):
dependencies:
react: 18.3.1
optionalDependencies:
- '@types/react': 18.3.5
+ '@types/react': 18.3.9
use-sync-external-store@1.2.2(react@18.3.1):
dependencies:
@@ -6695,17 +6714,17 @@ snapshots:
util-deprecate@1.0.2: {}
- vite-plugin-lib-inject-css@2.1.1(vite@5.4.4(lightningcss@1.27.0)):
+ vite-plugin-lib-inject-css@2.1.1(vite@5.4.8(lightningcss@1.27.0)):
dependencies:
'@ast-grep/napi': 0.22.6
magic-string: 0.30.11
picocolors: 1.1.0
- vite: 5.4.4(lightningcss@1.27.0)
+ vite: 5.4.8(lightningcss@1.27.0)
- vite@5.4.4(lightningcss@1.27.0):
+ vite@5.4.8(lightningcss@1.27.0):
dependencies:
esbuild: 0.21.5
- postcss: 8.4.45
+ postcss: 8.4.47
rollup: 4.21.2
optionalDependencies:
fsevents: 2.3.3
diff --git a/src/App.jsx b/src/App.jsx
index 7a4e277..4d34f75 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -67,9 +67,9 @@ import {
ResponsivePreview,
Modal,
Draggable,
- DraggableItem,
- DraggableItemHandle,
+ DraggableHandle,
ItemCollection,
+ OptionsPanelHeader,
} from '../lib';
import { icons } from '../lib/icons';
import { clsx } from 'clsx/lite';
@@ -384,6 +384,25 @@ function App() {
},
];
+ const repeaterDefaultItems2 = [
+ {
+ id: 'prvi',
+ title: 'Item 1',
+ icon: icons.num1Square,
+ },
+ {
+ id: 'drugi',
+ title: 'Item 2',
+ subtitle: 'Lorem',
+ icon: icons.num2Circle,
+ },
+ {
+ id: 'treci',
+ title: 'Item 3',
+ icon: icons.num3SquareAlt,
+ },
+ ];
+
const draggableListDefaultItems = [
{
toggle: false,
@@ -424,8 +443,9 @@ function App() {
];
const [repeaterItems, setRepeaterItems] = useState(repeaterDefaultItems);
- const [repeaterItems2, setRepeaterItems2] = useState(repeaterDefaultItems);
+ const [repeaterItems2, setRepeaterItems2] = useState(repeaterDefaultItems2);
const [draggableListItems, setDraggableListItems] = useState(draggableListDefaultItems);
+ const [draggableListItems2, setDraggableListItems2] = useState(draggableListDefaultItems);
const [draggableItems, setDraggableItems] = useState(draggableDefaultItems);
const [sliderValue, setSliderValue] = useState(0);
@@ -1723,7 +1743,7 @@ function App() {
{(item) => {
const { toggle, title, updateData } = item;
return (
-
-
+
+
{title}
updateData({ toggle: value })}
/>
-
+
);
}}
@@ -2777,8 +2796,43 @@ function App() {
);
}}
+
+
+ {(item) => {
+ const { toggle, title, icon, updateData } = item;
+
+ return (
+
+ updateData({ toggle: value })}
+ />
+
+ );
+ }}
+
-
+
+
+ Test
+ Save
+ >
+ }
+ >
+ Demo
+
+