diff --git a/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx b/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx index 71503d0c2c..826ff0e383 100644 --- a/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx +++ b/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx @@ -1,9 +1,9 @@ import { default as Pagination } from "antd/es/pagination"; import { EditorContext } from "comps/editorState"; import { BackgroundColorContext } from "comps/utils/backgroundColorContext"; -import _ from "lodash"; +import _, { findIndex } from "lodash"; import { ConstructorToView, deferAction } from "lowcoder-core"; -import { HintPlaceHolder, ScrollBar, pageItemRender } from "lowcoder-design"; +import { DragIcon, HintPlaceHolder, ScrollBar, pageItemRender } from "lowcoder-design"; import { RefObject, useContext, createContext, useMemo, useRef, useEffect } from "react"; import ReactResizeDetector from "react-resize-detector"; import styled from "styled-components"; @@ -22,6 +22,10 @@ import { useMergeCompStyles } from "@lowcoder-ee/util/hooks"; import { childrenToProps } from "@lowcoder-ee/comps/generators/multi"; import { AnimationStyleType } from "@lowcoder-ee/comps/controls/styleControlConstants"; import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils"; +import { DndContext } from "@dnd-kit/core"; +import { SortableContext, useSortable } from "@dnd-kit/sortable"; +import { CSS } from "@dnd-kit/utilities"; +import { JSONObject } from "@lowcoder-ee/index.sdk"; const ListViewWrapper = styled.div<{ $style: any; $paddingWidth: string,$animationStyle:AnimationStyleType }>` height: 100%; @@ -63,6 +67,22 @@ const ListOrientationWrapper = styled.div<{ flex-direction: ${(props) => (props.$isHorizontal ? "row" : "column")}; `; +const StyledDragIcon = styled(DragIcon)` + height: 16px; + width: 16px; + color: #8b8fa3; + + &:hover { + cursor: grab; + outline: none; + } + + &:focus { + cursor: grab; + outline: none; + } +`; + type MinHorizontalWidthContextType = { horizontalWidth: string, minHorizontalWidth?: string, @@ -73,19 +93,48 @@ const MinHorizontalWidthContext = createContext({ minHorizontalWidth: '100px', }); -const ContainerInListView = (props: ContainerBaseProps ) => { +const ContainerInListView = (props: ContainerBaseProps & {itemIdx: number, enableSorting?: boolean} ) => { const { horizontalWidth, minHorizontalWidth } = useContext(MinHorizontalWidthContext); + const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ + id: String(props.itemIdx), + }); + + if (!props.enableSorting) { + return ( +
+ +
+ ) + } + return (
+ {} void; minHorizontalWidth?: string; horizontalWidth: string; + enableSorting?: boolean; }; function ListItem({ @@ -122,6 +172,7 @@ function ListItem({ scrollContainerRef, minHeight, horizontalGridCells, + enableSorting, } = props; // disable the unmount function to save user's state with pagination @@ -133,35 +184,37 @@ function ListItem({ // }, []); return ( - + - ); @@ -190,6 +243,7 @@ export function ListView(props: Props) { () => getData(children.noOfRows.getView()), [children.noOfRows] ); + const listData = useMemo(() => children.listData.getView(), [children.listData]); const horizontalGridCells = useMemo(() => children.horizontalGridCells.getView(), [children.horizontalGridCells]); const autoHeight = useMemo(() => children.autoHeight.getView(), [children.autoHeight]); const showHorizontalScrollbar = useMemo(() => children.showHorizontalScrollbar.getView(), [children.showHorizontalScrollbar]); @@ -213,6 +267,13 @@ export function ListView(props: Props) { total, }; }, [children.pagination, totalCount]); + + const enableSorting = useMemo(() => children.enableSorting.getView(), [children.enableSorting]); + + useEffect(() => { + children.listData.dispatchChangeValueAction(data); + }, [JSON.stringify(data)]); + const style = children.style.getView(); const animationStyle = children.animationStyle.getView(); @@ -229,6 +290,7 @@ export function ListView(props: Props) { // log.log("List. listHeight: ", listHeight, " minHeight: ", minHeight); const renders = _.range(0, noOfRows).map((rowIdx) => { // log.log("renders. i: ", i, "containerProps: ", containerProps, " text: ", Object.values(containerProps.items as Record)[0].children.comp.children.text); + const items = _.range(0, noOfColumns); const render = (
- {_.range(0, noOfColumns).map((colIdx) => { + {items.map((colIdx) => { const itemIdx = rowIdx * noOfColumns + colIdx + pageInfo.offset; if ( itemIdx >= pageInfo.total || @@ -250,7 +312,7 @@ export function ListView(props: Props) { const containerProps = containerFn( { [itemIndexName]: itemIdx, - [itemDataName]: getCurrentItemParams(data, itemIdx) + [itemDataName]: getCurrentItemParams(listData as JSONObject[], itemIdx) }, String(itemIdx) ).getView(); @@ -259,6 +321,7 @@ export function ListView(props: Props) { deferAction(ContextContainerComp.batchDeleteAction([String(itemIdx)])) ); }; + return ( ); })}
); + return render; }); @@ -289,6 +354,23 @@ export function ListView(props: Props) { useMergeCompStyles(childrenProps, comp.dispatch); + const handleDragEnd = (e: { active: { id: string }; over: { id: string } | null }) => { + if (!e.over) { + return; + } + const fromIndex = Number(e.active.id); + const toIndex = Number(e.over.id); + if (fromIndex < 0 || toIndex < 0 || fromIndex === toIndex) { + return; + } + + const newData = [...listData]; + const [movedItem] = newData.splice(fromIndex, 1); + newData.splice(toIndex, 0, movedItem); + + children.listData.dispatchChangeValueAction(newData); + }; + // log.debug("renders: ", renders); return ( @@ -306,7 +388,20 @@ export function ListView(props: Props) { $isGrid={noOfColumns > 1} $autoHeight={autoHeight} > - {renders} + {!enableSorting + ? renders + : ( + + String(colIdx)) + } + > + {renders} + + + ) + } )} > diff --git a/client/packages/lowcoder/src/comps/comps/listViewComp/listViewComp.tsx b/client/packages/lowcoder/src/comps/comps/listViewComp/listViewComp.tsx index 47da5c6f3c..00f6807cf8 100644 --- a/client/packages/lowcoder/src/comps/comps/listViewComp/listViewComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/listViewComp/listViewComp.tsx @@ -29,7 +29,7 @@ import { withFunction, WrapContextNodeV2, } from "lowcoder-core"; -import { JSONValue } from "util/jsonTypes"; +import { JSONArray, JSONValue } from "util/jsonTypes"; import { depthEqual, lastValueIfEqual, shallowEqual } from "util/objectUtils"; import { CompTree, getAllCompItems, IContainer } from "../containerBase"; import { SimpleContainerComp, toSimpleContainerData } from "../containerBase/simpleContainerComp"; @@ -43,6 +43,7 @@ import { SliderControl } from "@lowcoder-ee/comps/controls/sliderControl"; const childrenMap = { noOfRows: withIsLoadingMethod(NumberOrJSONObjectArrayControl), // FIXME: migrate "noOfRows" to "data" + listData: stateComp([]), noOfColumns: withDefault(NumberControl, 1), itemIndexName: withDefault(StringControl, "i"), itemDataName: withDefault(StringControl, "currentItem"), @@ -60,6 +61,7 @@ const childrenMap = { animationStyle: styleControl(AnimationStyle, 'animationStyle'), horizontal: withDefault(BoolControl, false), minHorizontalWidth: withDefault(RadiusControl, '100px'), + enableSorting: withDefault(BoolControl, false), }; const ListViewTmpComp = new UICompBuilder(childrenMap, () => <>) @@ -116,7 +118,7 @@ export class ListViewImplComp extends ListViewTmpComp implements IContainer { const { itemCount } = getData(this.children.noOfRows.getView()); const itemIndexName = this.children.itemIndexName.getView(); const itemDataName = this.children.itemDataName.getView(); - const dataExposingNode = this.children.noOfRows.exposingNode(); + const dataExposingNode = this.children.listData.exposingNode(); const containerComp = this.children.container; // for each container expose each comps with params const exposingRecord = _(_.range(0, itemCount)) diff --git a/client/packages/lowcoder/src/comps/comps/listViewComp/listViewPropertyView.tsx b/client/packages/lowcoder/src/comps/comps/listViewComp/listViewPropertyView.tsx index 6e9a4b865d..22e288d737 100644 --- a/client/packages/lowcoder/src/comps/comps/listViewComp/listViewPropertyView.tsx +++ b/client/packages/lowcoder/src/comps/comps/listViewComp/listViewPropertyView.tsx @@ -57,6 +57,9 @@ export function listPropertyView(compType: ListCompType) {
{hiddenPropertyView(children)} {showDataLoadingIndicatorsPropertyView(children)} + {children.enableSorting.propertyView({ + label: trans('listView.enableSorting'), + })}
)} diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index ce47ff7093..825761bc3c 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -2598,7 +2598,8 @@ export const en = { "itemDataNameDesc": "The Variable Name Referring to the Item's Data Object, Default as {default}", "itemsDesc": "Exposing Data of Components in List", "dataDesc": "The JSON Data Used in the Current List", - "dataTooltip": "If You just Set a Number, This Field Will Be Regarded as Row Count, and the Data Will Be Regarded as Empty." + "dataTooltip": "If You just Set a Number, This Field Will Be Regarded as Row Count, and the Data Will Be Regarded as Empty.", + "enableSorting": "Allow Sorting" }, "navigation": { "addText": "Add Submenu Item", pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy