mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-18 07:52:23 +01:00
(wip) drag and drop
This commit is contained in:
parent
400f832313
commit
c8559c603c
@ -1,14 +1,19 @@
|
||||
// TODO: Remove next eslint-disable
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import React from 'react';
|
||||
import { ProductCategory } from '../../../models/product-category';
|
||||
import { useSortable } from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { ManageProductCategory } from './manage-product-category';
|
||||
import { DotsSixVertical } from 'phosphor-react';
|
||||
import { CaretDown, DotsSixVertical } from 'phosphor-react';
|
||||
|
||||
interface ProductCategoriesItemProps {
|
||||
productCategories: Array<ProductCategory>,
|
||||
category: ProductCategory,
|
||||
isChild?: boolean,
|
||||
offset: boolean,
|
||||
collapsed?: boolean,
|
||||
handleCollapse?: (id: number) => void,
|
||||
status: 'child' | 'single' | 'parent',
|
||||
onSuccess: (message: string) => void,
|
||||
onError: (message: string) => void,
|
||||
}
|
||||
@ -16,41 +21,53 @@ interface ProductCategoriesItemProps {
|
||||
/**
|
||||
* Renders a draggable category item
|
||||
*/
|
||||
export const ProductCategoriesItem: React.FC<ProductCategoriesItemProps> = ({ productCategories, category, isChild, onSuccess, onError }) => {
|
||||
export const ProductCategoriesItem: React.FC<ProductCategoriesItemProps> = ({ productCategories, category, offset, collapsed, handleCollapse, status, onSuccess, onError }) => {
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
transform,
|
||||
transition
|
||||
transition,
|
||||
isDragging
|
||||
} = useSortable({ id: category.id });
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition
|
||||
transition,
|
||||
transform: CSS.Transform.toString(transform)
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={setNodeRef} style={style} className={`product-categories-item ${isChild ? 'is-child' : ''}`}>
|
||||
<div className='itemInfo'>
|
||||
<p className='itemInfo-name'>{category.name}</p>
|
||||
<span className='itemInfo-count'>[count]</span>
|
||||
</div>
|
||||
<div className='actions'>
|
||||
<div className='manage'>
|
||||
<ManageProductCategory action='update'
|
||||
productCategories={productCategories}
|
||||
productCategory={category}
|
||||
onSuccess={onSuccess} onError={onError} />
|
||||
<ManageProductCategory action='delete'
|
||||
productCategories={productCategories}
|
||||
productCategory={category}
|
||||
onSuccess={onSuccess} onError={onError} />
|
||||
<div ref={setNodeRef} style={style}
|
||||
className={`product-categories-item ${(status === 'child' && collapsed) ? 'is-collapsed' : ''}`}>
|
||||
{(status === 'child' || offset) &&
|
||||
<div className='offset'></div>
|
||||
}
|
||||
<div className="wrap">
|
||||
<div className='itemInfo'>
|
||||
{status === 'parent' && <div className='collapse-handle'>
|
||||
<button className={collapsed ? '' : 'rotate'} onClick={() => handleCollapse(category.id)}>
|
||||
<CaretDown size={16} weight="bold" />
|
||||
</button>
|
||||
</div>}
|
||||
<p className='itemInfo-name'>{category.name}</p>
|
||||
<span className='itemInfo-count'>[count]</span>
|
||||
</div>
|
||||
<div>
|
||||
<button {...attributes} {...listeners} className='draghandle'>
|
||||
<DotsSixVertical size={16} />
|
||||
</button>
|
||||
<div className='actions'>
|
||||
<div className='manage'>
|
||||
<ManageProductCategory action='update'
|
||||
productCategories={productCategories}
|
||||
productCategory={category}
|
||||
onSuccess={onSuccess} onError={onError} />
|
||||
<ManageProductCategory action='delete'
|
||||
productCategories={productCategories}
|
||||
productCategory={category}
|
||||
onSuccess={onSuccess} onError={onError} />
|
||||
</div>
|
||||
<div className='drag-handle'>
|
||||
<button {...attributes} {...listeners}>
|
||||
<DotsSixVertical size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,9 +1,11 @@
|
||||
// TODO: Remove next eslint-disable
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useImmer } from 'use-immer';
|
||||
import { ProductCategory } from '../../../models/product-category';
|
||||
import { DndContext, KeyboardSensor, PointerSensor, useSensor, useSensors, closestCenter, DragMoveEvent } from '@dnd-kit/core';
|
||||
import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
||||
import { restrictToWindowEdges } from '@dnd-kit/modifiers';
|
||||
import { ProductCategoriesItem } from './product-categories-item';
|
||||
|
||||
interface ProductCategoriesTreeProps {
|
||||
@ -18,13 +20,18 @@ interface ProductCategoriesTreeProps {
|
||||
*/
|
||||
export const ProductCategoriesTree: React.FC<ProductCategoriesTreeProps> = ({ productCategories, onDnd, onSuccess, onError }) => {
|
||||
const [categoriesList, setCategoriesList] = useImmer<ProductCategory[]>(productCategories);
|
||||
const [hiddenChildren, setHiddenChildren] = useState({});
|
||||
const [activeData, setActiveData] = useImmer<ActiveData>(initActiveData);
|
||||
// TODO: type extractedChildren: {[parentId]: ProductCategory[]} ???
|
||||
const [extractedChildren, setExtractedChildren] = useImmer({});
|
||||
const [collapsed, setCollapsed] = useImmer<number[]>([]);
|
||||
const [offset, setOffset] = useState<boolean>(false);
|
||||
|
||||
// Initialize state from props, sorting list as a tree
|
||||
// Initialize state from props
|
||||
useEffect(() => {
|
||||
setCategoriesList(productCategories);
|
||||
}, [productCategories]);
|
||||
|
||||
// Dnd Kit config
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, {
|
||||
@ -34,145 +41,238 @@ export const ProductCategoriesTree: React.FC<ProductCategoriesTreeProps> = ({ pr
|
||||
|
||||
/**
|
||||
* On drag start
|
||||
* Collect dragged items' data
|
||||
* Extract children from list
|
||||
*/
|
||||
const handleDragStart = ({ active }: DragMoveEvent) => {
|
||||
hideChildren(active.id, categoriesList.findIndex(el => el.id === active.id));
|
||||
const activeChildren = categoriesList.filter(c => c.parent_id === active.id);
|
||||
if (activeChildren.length) {
|
||||
setHiddenChildren({ [active.id]: activeChildren });
|
||||
const activeIndex = categoriesList.findIndex(el => el.id === active.id);
|
||||
const tmpList = [...categoriesList];
|
||||
tmpList.splice(activeIndex + 1, activeChildren.length);
|
||||
setCategoriesList(tmpList);
|
||||
}
|
||||
const activeIndex = active.data.current.sortable.index;
|
||||
const children = getChildren(active.id);
|
||||
|
||||
setActiveData(draft => {
|
||||
draft.index = activeIndex;
|
||||
draft.category = getCategory(active.id);
|
||||
draft.status = getStatus(active.id);
|
||||
draft.children = children?.length ? children : null;
|
||||
});
|
||||
|
||||
setExtractedChildren(draft => { draft[active.id] = children; });
|
||||
hideChildren(active.id, activeIndex);
|
||||
};
|
||||
|
||||
/**
|
||||
* On drag move
|
||||
*/
|
||||
const handleDragMove = ({ delta, over }: DragMoveEvent) => {
|
||||
console.log(findCategory(over.id).name);
|
||||
if (delta.x > 48) {
|
||||
console.log('Child');
|
||||
} else {
|
||||
console.log('Parent');
|
||||
const handleDragMove = ({ delta, active, over }: DragMoveEvent) => {
|
||||
if ((getStatus(active.id) === 'single' || getStatus(active.id) === 'child') && getStatus(over.id) === 'single') {
|
||||
if (delta.x > 32) {
|
||||
setOffset(true);
|
||||
} else {
|
||||
setOffset(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update categories list after an item was dropped
|
||||
* On drag End
|
||||
* Insert children back in list
|
||||
*/
|
||||
|
||||
const handleDragEnd = ({ active, over }: DragMoveEvent) => {
|
||||
let newOrder = [...categoriesList];
|
||||
const currentIdsOrder = over?.data.current.sortable.items;
|
||||
let newIndex = over.data.current.sortable.index;
|
||||
|
||||
// si déplacé sur une autre catégorie…
|
||||
if (active.id !== over.id) {
|
||||
// liste d'ids des catégories visibles
|
||||
const previousIdsOrder = over?.data.current.sortable.items;
|
||||
// index dans previousIdsOrder de la catégorie déplacée
|
||||
const oldIndex = active.data.current.sortable.index;
|
||||
// index dans previousIdsOrder de la catégorie de réception
|
||||
const newIndex = over.data.current.sortable.index;
|
||||
// liste de catégories mise à jour après le drop
|
||||
const newIdsOrder = arrayMove(previousIdsOrder, oldIndex, newIndex);
|
||||
// id du parent de la catégorie de réception
|
||||
const newParentId = categoriesList[newIndex].parent_id;
|
||||
|
||||
// nouvelle liste de catégories classées par newIdsOrder
|
||||
// [A] Single |> [B] Single
|
||||
if (getStatus(active.id) === 'single' && getStatus(over.id) === 'single') {
|
||||
console.log('[A] Single |> [B] Single');
|
||||
const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex);
|
||||
newOrder = newIdsOrder.map(sortedId => {
|
||||
// catégorie courante du map retrouvée grâce à l'id
|
||||
const categoryFromId = findCategory(sortedId);
|
||||
// si catégorie courante = catégorie déplacée…
|
||||
if (categoryFromId.id === active.id) {
|
||||
// maj du parent
|
||||
categoryFromId.parent_id = newParentId;
|
||||
let category = getCategory(sortedId);
|
||||
if (offset && sortedId === active.id && activeData.index < newIndex) {
|
||||
category = { ...category, parent_id: Number(over.id) };
|
||||
}
|
||||
// retour de la catégorie courante
|
||||
return categoryFromId;
|
||||
return category;
|
||||
});
|
||||
}
|
||||
// insert siblings back
|
||||
if (hiddenChildren[active.id]?.length) {
|
||||
newOrder.splice(over.data.current.sortable.index + 1, 0, ...hiddenChildren[active.id]);
|
||||
setHiddenChildren({ ...hiddenChildren, [active.id]: null });
|
||||
|
||||
// [A] Child |> [B] Single
|
||||
if ((getStatus(active.id) === 'child') && getStatus(over.id) === 'single') {
|
||||
console.log('[A] Child |> [B] Single');
|
||||
const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex);
|
||||
newOrder = newIdsOrder.map(sortedId => {
|
||||
let category = getCategory(sortedId);
|
||||
if (offset && sortedId === active.id && activeData.index < newIndex) {
|
||||
category = { ...category, parent_id: Number(over.id) };
|
||||
} else if (sortedId === active.id && activeData.index < newIndex) {
|
||||
category = { ...category, parent_id: null };
|
||||
}
|
||||
return category;
|
||||
});
|
||||
}
|
||||
|
||||
// [A] Single || Child |>…
|
||||
if (getStatus(active.id) === 'single' || getStatus(active.id) === 'child') {
|
||||
// [B] Parent
|
||||
if (getStatus(over.id) === 'parent') {
|
||||
if (activeData.index < newIndex) {
|
||||
const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex);
|
||||
newOrder = newIdsOrder.map(sortedId => {
|
||||
let category = getCategory(sortedId);
|
||||
if (sortedId === active.id) {
|
||||
category = { ...category, parent_id: Number(over.id) };
|
||||
}
|
||||
return category;
|
||||
});
|
||||
} else {
|
||||
const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex);
|
||||
newOrder = newIdsOrder.map(sortedId => {
|
||||
let category = getCategory(sortedId);
|
||||
if (sortedId === active.id) {
|
||||
category = { ...category, parent_id: null };
|
||||
}
|
||||
return category;
|
||||
});
|
||||
}
|
||||
}
|
||||
// [B] Child
|
||||
if (getStatus(over.id) === 'child') {
|
||||
const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex);
|
||||
newOrder = newIdsOrder.map(sortedId => {
|
||||
let category = getCategory(sortedId);
|
||||
if (sortedId === active.id) {
|
||||
category = { ...category, parent_id: getCategory(over.id).parent_id };
|
||||
}
|
||||
return category;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// [A] Parent |>…
|
||||
if (getStatus(active.id) === 'parent') {
|
||||
// [B] Single
|
||||
if (getStatus(over.id) === 'single') {
|
||||
const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex);
|
||||
newOrder = newIdsOrder.map(sortedId => getCategory(sortedId));
|
||||
}
|
||||
// [B] Parent
|
||||
if (getStatus(over.id) === 'parent') {
|
||||
if (activeData.index < newIndex) {
|
||||
const lastOverChildIndex = newOrder.findIndex(c => c.id === getChildren(over.id).pop().id);
|
||||
newIndex = lastOverChildIndex;
|
||||
const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex);
|
||||
newOrder = newIdsOrder.map(sortedId => getCategory(sortedId));
|
||||
} else {
|
||||
const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex);
|
||||
newOrder = newIdsOrder.map(sortedId => getCategory(sortedId));
|
||||
}
|
||||
}
|
||||
// [B] Child
|
||||
if (getStatus(over.id) === 'child') {
|
||||
if (activeData.index < newIndex) {
|
||||
const parent = newOrder.find(c => c.id === getCategory(over.id).parent_id);
|
||||
const lastSiblingIndex = newOrder.findIndex(c => c.id === getChildren(parent.id).pop().id);
|
||||
newIndex = lastSiblingIndex;
|
||||
const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex);
|
||||
newOrder = newIdsOrder.map(sortedId => getCategory(sortedId));
|
||||
} else {
|
||||
const parentIndex = currentIdsOrder.indexOf(getCategory(over.id).parent_id);
|
||||
newIndex = parentIndex;
|
||||
const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex);
|
||||
newOrder = newIdsOrder.map(sortedId => getCategory(sortedId));
|
||||
}
|
||||
}
|
||||
// insert children back
|
||||
newOrder = showChildren(active.id, newOrder, newIndex);
|
||||
}
|
||||
|
||||
onDnd(newOrder);
|
||||
setOffset(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset state if the drag was canceled
|
||||
* On drag cancel
|
||||
* Reset states
|
||||
*/
|
||||
const handleDragCancel = ({ active }: DragMoveEvent) => {
|
||||
setHiddenChildren({ ...hiddenChildren, [active.id]: null });
|
||||
setCategoriesList(productCategories);
|
||||
setActiveData(initActiveData);
|
||||
setExtractedChildren({ ...extractedChildren, [active.id]: null });
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide children by their parent's id
|
||||
* Get a category by its id
|
||||
*/
|
||||
const hideChildren = (parentId, parentIndex) => {
|
||||
const children = findChildren(parentId);
|
||||
if (children?.length) {
|
||||
const tmpList = [...categoriesList];
|
||||
tmpList.splice(parentIndex + 1, children.length);
|
||||
setCategoriesList(tmpList);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Find a category by its id
|
||||
*/
|
||||
const findCategory = (id) => {
|
||||
const getCategory = (id) => {
|
||||
return categoriesList.find(c => c.id === id);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the children categories of a parent category by its id
|
||||
* Get the children categories of a parent category by its id
|
||||
*/
|
||||
const findChildren = (id) => {
|
||||
const getChildren = (id) => {
|
||||
const displayedChildren = categoriesList.filter(c => c.parent_id === id);
|
||||
if (displayedChildren.length) {
|
||||
return displayedChildren;
|
||||
}
|
||||
return hiddenChildren[id];
|
||||
return extractedChildren[id];
|
||||
};
|
||||
|
||||
/**
|
||||
* Find category's status by its id
|
||||
* single | parent | child
|
||||
* Get category's status by its id
|
||||
* child | single | parent
|
||||
*/
|
||||
const categoryStatus = (id) => {
|
||||
const c = findCategory(id);
|
||||
if (!c.parent_id) {
|
||||
if (findChildren(id)?.length) {
|
||||
return 'parent';
|
||||
}
|
||||
return 'single';
|
||||
} else {
|
||||
return 'child';
|
||||
const getStatus = (id) => {
|
||||
const c = getCategory(id);
|
||||
return !c.parent_id
|
||||
? getChildren(id)?.length
|
||||
? 'parent'
|
||||
: 'single'
|
||||
: 'child';
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract children from the list by their parent's id
|
||||
*/
|
||||
const hideChildren = (parentId, parentIndex) => {
|
||||
const children = getChildren(parentId);
|
||||
if (children?.length) {
|
||||
const shortenList = [...categoriesList];
|
||||
shortenList.splice(parentIndex + 1, children.length);
|
||||
setCategoriesList(shortenList);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Translate visual order into categories data positions
|
||||
* Insert children back in the list by their parent's id
|
||||
*/
|
||||
const indexToPosition = (sortedIds: number[]) => {
|
||||
const sort = sortedIds.map(sortedId => categoriesList.find(el => el.id === sortedId));
|
||||
const newPositions = sort.map(c => {
|
||||
if (typeof c.parent_id === 'number') {
|
||||
const parentIndex = sort.findIndex(el => el.id === c.parent_id);
|
||||
const currentIndex = sort.findIndex(el => el.id === c.id);
|
||||
return { ...c, position: (currentIndex - parentIndex - 1) };
|
||||
}
|
||||
return c;
|
||||
});
|
||||
return newPositions;
|
||||
const showChildren = (parentId, currentList, insertIndex) => {
|
||||
if (extractedChildren[parentId]?.length) {
|
||||
currentList.splice(insertIndex + 1, 0, ...extractedChildren[parentId]);
|
||||
setExtractedChildren({ ...extractedChildren, [parentId]: null });
|
||||
}
|
||||
return currentList;
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle parent category by hidding/showing its children
|
||||
*/
|
||||
const handleCollapse = (id) => {
|
||||
const i = collapsed.findIndex(el => el === id);
|
||||
if (i === -1) {
|
||||
setCollapsed([...collapsed, id]);
|
||||
} else {
|
||||
const copy = [...collapsed];
|
||||
copy.splice(i, 1);
|
||||
setCollapsed(copy);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
modifiers={[restrictToWindowEdges]}
|
||||
onDragStart={handleDragStart}
|
||||
onDragMove={handleDragMove}
|
||||
onDragEnd={handleDragEnd}
|
||||
onDragCancel={handleDragCancel}
|
||||
>
|
||||
@ -185,7 +285,10 @@ export const ProductCategoriesTree: React.FC<ProductCategoriesTreeProps> = ({ pr
|
||||
category={category}
|
||||
onSuccess={onSuccess}
|
||||
onError={onError}
|
||||
isChild={typeof category.parent_id === 'number'}
|
||||
offset={category.id === activeData.category?.id && activeData?.offset}
|
||||
collapsed={collapsed.includes(category.id) || collapsed.includes(category.parent_id)}
|
||||
handleCollapse={handleCollapse}
|
||||
status={getStatus(category.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -193,3 +296,18 @@ export const ProductCategoriesTree: React.FC<ProductCategoriesTreeProps> = ({ pr
|
||||
</DndContext>
|
||||
);
|
||||
};
|
||||
|
||||
interface ActiveData {
|
||||
index: number,
|
||||
category: ProductCategory,
|
||||
status: 'child' | 'single' | 'parent',
|
||||
children: ProductCategory[],
|
||||
offset: boolean
|
||||
}
|
||||
const initActiveData: ActiveData = {
|
||||
index: null,
|
||||
category: null,
|
||||
status: null,
|
||||
children: [],
|
||||
offset: false
|
||||
};
|
||||
|
35
app/frontend/src/stylesheets/modules/store/dropOptions.md
Normal file
35
app/frontend/src/stylesheets/modules/store/dropOptions.md
Normal file
@ -0,0 +1,35 @@
|
||||
<!-- Drop options -->
|
||||
|
||||
## [A] Single |> [B] Single
|
||||
[A] = index de [B]
|
||||
offset && [A] child de [B]
|
||||
|
||||
<!--## [A] Single || Child |> [B] Parent
|
||||
[A] = index de [B]
|
||||
[A] child de [B]-->
|
||||
|
||||
<!--## [A] Single || Child |> [B] Child
|
||||
[A] = index de [B]
|
||||
[A] même parent que [B]-->
|
||||
|
||||
## [A] Child |> [B] Single
|
||||
[A] = index de [B]
|
||||
offset
|
||||
? [A] child de [B]
|
||||
: [A] Single
|
||||
|
||||
<!--## [A] Parent |> [B] Single
|
||||
[A] = index de [B]-->
|
||||
|
||||
<!--## [A] Parent |> [B] Parent
|
||||
down
|
||||
? [A] = index du dernier child de [B]
|
||||
: [A] = index de [B]-->
|
||||
|
||||
<!--## [A] Parent |> [B] Child
|
||||
down
|
||||
? [A] = index du dernier child de [B]
|
||||
: [A] = index du parent de [B]-->
|
||||
|
||||
## [A] Single |> [A]
|
||||
offset && [A] child du précédant parent
|
@ -41,50 +41,80 @@
|
||||
}
|
||||
&-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.6rem 1.6rem;
|
||||
border: 1px solid var(--gray-soft-dark);
|
||||
border-radius: var(--border-radius);
|
||||
pointer-events: all;
|
||||
|
||||
.itemInfo {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
&-name {
|
||||
margin: 0;
|
||||
@include text-base;
|
||||
font-weight: 600;
|
||||
color: var(--gray-hard-darkest);
|
||||
}
|
||||
&-count {
|
||||
margin-left: 2.4rem;
|
||||
@include text-sm;
|
||||
font-weight: 500;
|
||||
color: var(--information);
|
||||
}
|
||||
&.is-collapsed {
|
||||
height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
.offset {
|
||||
width: 4.8rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
.wrap {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.manage {
|
||||
overflow: hidden;
|
||||
padding: 0.6rem 1.6rem;
|
||||
border: 1px solid var(--gray-soft-dark);
|
||||
border-radius: var(--border-radius);
|
||||
background-color: var(--gray-soft-lightest);
|
||||
.itemInfo {
|
||||
display: flex;
|
||||
border-radius: var(--border-radius-sm);
|
||||
button {
|
||||
@include btn;
|
||||
border-radius: 0;
|
||||
color: var(--gray-soft-lightest);
|
||||
&:hover { opacity: 0.75; }
|
||||
align-items: center;
|
||||
&-name {
|
||||
margin: 0;
|
||||
@include text-base;
|
||||
font-weight: 600;
|
||||
color: var(--gray-hard-darkest);
|
||||
}
|
||||
&-count {
|
||||
margin-left: 2.4rem;
|
||||
@include text-sm;
|
||||
font-weight: 500;
|
||||
color: var(--information);
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
.manage {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
border-radius: var(--border-radius-sm);
|
||||
button {
|
||||
@include btn;
|
||||
border-radius: 0;
|
||||
color: var(--gray-soft-lightest);
|
||||
&:hover { opacity: 0.75; }
|
||||
}
|
||||
.edit-btn { background: var(--gray-hard-darkest); }
|
||||
.delete-btn { background: var(--error); }
|
||||
}
|
||||
.edit-btn {background: var(--gray-hard-darkest) }
|
||||
.delete-btn {background: var(--error) }
|
||||
}
|
||||
}
|
||||
|
||||
.draghandle {
|
||||
.collapse-handle {
|
||||
width: 4rem;
|
||||
margin: 0 0 0 -1rem;
|
||||
button {
|
||||
@include btn;
|
||||
background: none;
|
||||
border-radius: 0;
|
||||
transition: transform 250ms ease-in-out;
|
||||
&.rotate {
|
||||
transform: rotateZ(-180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
.drag-handle button {
|
||||
@include btn;
|
||||
cursor: grab;
|
||||
}
|
||||
|
@ -48,6 +48,7 @@
|
||||
"@babel/runtime": "^7.17.2",
|
||||
"@claviska/jquery-minicolors": "^2.3.5",
|
||||
"@dnd-kit/core": "^6.0.5",
|
||||
"@dnd-kit/modifiers": "^6.0.0",
|
||||
"@dnd-kit/sortable": "^7.0.1",
|
||||
"@fortawesome/fontawesome-free": "5.14.0",
|
||||
"@lyracom/embedded-form-glue": "^0.3.3",
|
||||
|
@ -1504,6 +1504,14 @@
|
||||
"@dnd-kit/utilities" "^3.2.0"
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@dnd-kit/modifiers@^6.0.0":
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@dnd-kit/modifiers/-/modifiers-6.0.0.tgz#61d8834132f791a68e9e93be5426becbcd45c078"
|
||||
integrity sha512-V3+JSo6/BTcgPRHiNUTSKgqVv/doKXg+T4Z0QvKiiXp+uIyJTUtPkQOBRQApUWi3ApBhnoWljyt/3xxY4fTd0Q==
|
||||
dependencies:
|
||||
"@dnd-kit/utilities" "^3.2.0"
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@dnd-kit/sortable@^7.0.1":
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@dnd-kit/sortable/-/sortable-7.0.1.tgz#99c6012bbab4d8bb726c0eef7b921a338c404fdb"
|
||||
|
Loading…
x
Reference in New Issue
Block a user