mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-11 03:24:16 +00:00
💄Added SortableList
This commit is contained in:
parent
42578bb08a
commit
74c20d3b24
@ -34,11 +34,14 @@
|
|||||||
"@capacitor/storage": "1.2.4",
|
"@capacitor/storage": "1.2.4",
|
||||||
"@capgo/capacitor-updater": "^4.12.8",
|
"@capgo/capacitor-updater": "^4.12.8",
|
||||||
"@corenode/utils": "0.28.26",
|
"@corenode/utils": "0.28.26",
|
||||||
|
"@dnd-kit/core": "^6.0.8",
|
||||||
|
"@dnd-kit/sortable": "^7.0.2",
|
||||||
"@emotion/css": "11.0.0",
|
"@emotion/css": "11.0.0",
|
||||||
"@emotion/react": "^11.10.5",
|
"@emotion/react": "^11.10.5",
|
||||||
"@emotion/styled": "^11.10.5",
|
"@emotion/styled": "^11.10.5",
|
||||||
"@loadable/component": "5.15.2",
|
"@loadable/component": "5.15.2",
|
||||||
"@mui/material": "^5.11.9",
|
"@mui/material": "^5.11.9",
|
||||||
|
"@paciolan/remote-component": "^2.13.0",
|
||||||
"@tsmx/human-readable": "^1.0.7",
|
"@tsmx/human-readable": "^1.0.7",
|
||||||
"antd": "^5.2.1",
|
"antd": "^5.2.1",
|
||||||
"antd-mobile": "^5.0.0-rc.17",
|
"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