mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
💄Added SortableList
This commit is contained in:
parent
42578bb08a
commit
74c20d3b24
@ -34,11 +34,14 @@
|
||||
"@capacitor/storage": "1.2.4",
|
||||
"@capgo/capacitor-updater": "^4.12.8",
|
||||
"@corenode/utils": "0.28.26",
|
||||
"@dnd-kit/core": "^6.0.8",
|
||||
"@dnd-kit/sortable": "^7.0.2",
|
||||
"@emotion/css": "11.0.0",
|
||||
"@emotion/react": "^11.10.5",
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@loadable/component": "5.15.2",
|
||||
"@mui/material": "^5.11.9",
|
||||
"@paciolan/remote-component": "^2.13.0",
|
||||
"@tsmx/human-readable": "^1.0.7",
|
||||
"antd": "^5.2.1",
|
||||
"antd-mobile": "^5.0.0-rc.17",
|
||||
@ -171,4 +174,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
268
packages/app/src/components/SortableList/index.jsx
Normal file
268
packages/app/src/components/SortableList/index.jsx
Normal file
@ -0,0 +1,268 @@
|
||||
import React from "react"
|
||||
import { Button } from "antd"
|
||||
import { Icons, createIconRender } from "components/Icons"
|
||||
import classnames from "classnames"
|
||||
import useLongPress from "hooks/useLongPress"
|
||||
|
||||
import {
|
||||
DndContext,
|
||||
TouchSensor,
|
||||
MouseSensor,
|
||||
KeyboardSensor,
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
DragOverlay,
|
||||
defaultDropAnimationSideEffects,
|
||||
} from "@dnd-kit/core"
|
||||
import {
|
||||
SortableContext,
|
||||
arrayMove,
|
||||
sortableKeyboardCoordinates,
|
||||
useSortable,
|
||||
} from "@dnd-kit/sortable"
|
||||
import { CSS } from "@dnd-kit/utilities"
|
||||
|
||||
import "./index.less"
|
||||
|
||||
export const SortableItemChildrenContext = React.createContext({
|
||||
attributes: {},
|
||||
listeners: undefined,
|
||||
ref() { },
|
||||
activeDrag: true,
|
||||
})
|
||||
|
||||
export const SortableItemContext = React.createContext({
|
||||
activeDrag: true,
|
||||
onLongPress() { },
|
||||
setActiveDrag() { },
|
||||
})
|
||||
|
||||
export function DragHandle() {
|
||||
const { attributes, listeners, ref, activeDrag } = React.useContext(SortableItemChildrenContext)
|
||||
|
||||
return (
|
||||
<button
|
||||
className={classnames(
|
||||
"drag-handle",
|
||||
{
|
||||
["active"]: activeDrag
|
||||
}
|
||||
)}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
ref={ref}
|
||||
>
|
||||
<svg viewBox="0 0 20 20" width="12">
|
||||
<path d="M7 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 2zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 14zm6-8a2 2 0 1 0-.001-4.001A2 2 0 0 0 13 6zm0 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 14z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export function SortableOverlay({ children }) {
|
||||
const dropAnimationConfig = {
|
||||
sideEffects: defaultDropAnimationSideEffects({
|
||||
styles: {
|
||||
active: {
|
||||
opacity: "0.4"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<DragOverlay dropAnimation={dropAnimationConfig}>{children}</DragOverlay>
|
||||
)
|
||||
}
|
||||
|
||||
export function SortableItem({
|
||||
children,
|
||||
id,
|
||||
}) {
|
||||
const { activeDrag, onLongPress } = React.useContext(SortableItemContext)
|
||||
|
||||
const {
|
||||
attributes,
|
||||
isDragging,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
setActivatorNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
} = useSortable({ id })
|
||||
|
||||
const context = React.useMemo(
|
||||
() => ({
|
||||
attributes,
|
||||
listeners,
|
||||
ref: setActivatorNodeRef,
|
||||
activeDrag: activeDrag
|
||||
}),
|
||||
[attributes, listeners, setActivatorNodeRef, activeDrag]
|
||||
)
|
||||
|
||||
const style = {
|
||||
opacity: isDragging ? 0.4 : undefined,
|
||||
transform: CSS.Translate.toString(transform),
|
||||
transition,
|
||||
}
|
||||
|
||||
return (
|
||||
<SortableItemChildrenContext.Provider value={context}>
|
||||
<li
|
||||
className="sortable-item"
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
{...useLongPress(onLongPress)}
|
||||
>
|
||||
{children}
|
||||
<DragHandle />
|
||||
</li>
|
||||
</SortableItemChildrenContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const DragActiveActions = ({
|
||||
actions
|
||||
}) => {
|
||||
const { activeDrag, setActiveDrag } = React.useContext(SortableItemContext)
|
||||
|
||||
return <div
|
||||
className={classnames(
|
||||
"drag-actions",
|
||||
{
|
||||
["active"]: activeDrag
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon={<Icons.Check />}
|
||||
onClick={() => setActiveDrag(false)}
|
||||
/>
|
||||
|
||||
{
|
||||
actions?.map((action, index) => {
|
||||
return <Button
|
||||
key={index}
|
||||
type={action.type ?? "default"}
|
||||
size="small"
|
||||
icon={createIconRender(action.icon)}
|
||||
onClick={action.onClick}
|
||||
disabled={action.disabled}
|
||||
danger={action.danger}
|
||||
>
|
||||
{action.label}
|
||||
</Button>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
export const SortableList = ({
|
||||
items,
|
||||
onChange,
|
||||
renderItem,
|
||||
activationConstraint,
|
||||
useDragOverlay,
|
||||
useActiveDragActions = true,
|
||||
activeDragActions,
|
||||
}) => {
|
||||
const [active, setActive] = React.useState(null)
|
||||
const [activeDrag, setActiveDrag] = React.useState(false)
|
||||
|
||||
const activeItem = React.useMemo(() => items.find((item) => item.id === active?.id), [active, items])
|
||||
|
||||
const context = React.useMemo(
|
||||
() => ({
|
||||
activeDrag,
|
||||
onLongPress: () => setActiveDrag(true),
|
||||
setActiveDrag: setActiveDrag,
|
||||
}),
|
||||
)
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(MouseSensor, {
|
||||
activationConstraint,
|
||||
}),
|
||||
useSensor(TouchSensor, {
|
||||
activationConstraint,
|
||||
}),
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, {
|
||||
coordinateGetter: sortableKeyboardCoordinates,
|
||||
})
|
||||
)
|
||||
|
||||
const onDragStart = ({ active }) => {
|
||||
if (!activeDrag) {
|
||||
return
|
||||
}
|
||||
|
||||
setActive(active)
|
||||
}
|
||||
|
||||
const onDragEnd = ({ active, over }) => {
|
||||
if (!activeDrag) {
|
||||
return
|
||||
}
|
||||
|
||||
if (over && active.id !== over?.id) {
|
||||
const activeIndex = items.findIndex(({ id }) => id === active.id);
|
||||
const overIndex = items.findIndex(({ id }) => id === over.id);
|
||||
|
||||
onChange(arrayMove(items, activeIndex, overIndex));
|
||||
}
|
||||
|
||||
setActive(null);
|
||||
}
|
||||
|
||||
const onDragCancel = () => {
|
||||
setActive(null)
|
||||
}
|
||||
|
||||
return <DndContext
|
||||
sensors={sensors}
|
||||
onDragStart={onDragStart}
|
||||
onDragEnd={onDragEnd}
|
||||
onDragCancel={onDragCancel}
|
||||
>
|
||||
<SortableContext items={items}>
|
||||
<SortableItemContext.Provider value={context}>
|
||||
<ul
|
||||
className={classnames(
|
||||
"shortable-list",
|
||||
{
|
||||
["active-drag"]: activeDrag
|
||||
}
|
||||
)}
|
||||
role="application"
|
||||
>
|
||||
{
|
||||
items.map((item, index) => (
|
||||
<React.Fragment key={item.id}>
|
||||
{
|
||||
renderItem(item, index)
|
||||
}
|
||||
</React.Fragment>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
|
||||
{
|
||||
useActiveDragActions && <DragActiveActions
|
||||
actions={activeDragActions}
|
||||
/>
|
||||
}
|
||||
</SortableItemContext.Provider>
|
||||
</SortableContext>
|
||||
|
||||
{
|
||||
useDragOverlay && <SortableOverlay>
|
||||
{activeItem ? renderItem(activeItem) : null}
|
||||
</SortableOverlay>
|
||||
}
|
||||
</DndContext>
|
||||
}
|
109
packages/app/src/components/SortableList/index.less
Normal file
109
packages/app/src/components/SortableList/index.less
Normal file
@ -0,0 +1,109 @@
|
||||
.shortable-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
gap: 10px;
|
||||
padding: 0;
|
||||
|
||||
list-style: none;
|
||||
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&.active-drag {
|
||||
//border: 1px dashed var(--border-color);
|
||||
border-radius: 12px;
|
||||
|
||||
padding: 10px;
|
||||
|
||||
background-color: var(--background-color-accent);
|
||||
}
|
||||
}
|
||||
|
||||
.sortable-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
|
||||
//box-shadow: 0 0 0 calc(1px / var(--scale-x, 1)) rgba(63, 63, 68, 0.05), 0 1px calc(3px / var(--scale-x, 1)) 0 rgba(34, 33, 81, 0.15);
|
||||
//border-radius: calc(4px / var(--scale-x, 1));
|
||||
|
||||
box-sizing: border-box;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
display: flex;
|
||||
|
||||
width: 0px;
|
||||
padding: 0px;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
flex: 0 0 auto;
|
||||
|
||||
touch-action: none;
|
||||
|
||||
cursor: var(--cursor, pointer);
|
||||
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
appearance: none;
|
||||
background-color: transparent;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&.active {
|
||||
width: 12px;
|
||||
padding: 15px;
|
||||
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
svg {
|
||||
flex: 0 0 auto;
|
||||
|
||||
margin: auto;
|
||||
|
||||
height: 100%;
|
||||
|
||||
overflow: visible;
|
||||
|
||||
fill: #919eab;
|
||||
}
|
||||
}
|
||||
|
||||
.drag-actions {
|
||||
display: flex;
|
||||
|
||||
flex-direction: row;
|
||||
|
||||
align-items: center;
|
||||
|
||||
gap: 10px;
|
||||
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&.active {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user