From 1c8fd39b5202f20dd3c10a824503b8b76cdbcd30 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Wed, 16 Apr 2025 01:09:01 +0500 Subject: [PATCH 1/2] added sorting option in list view comp --- .../src/comps/comps/listViewComp/listView.tsx | 135 +++++++++++++----- .../comps/comps/listViewComp/listViewComp.tsx | 5 +- 2 files changed, 104 insertions(+), 36 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx b/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx index 71503d0c2c..a51175b049 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,11 @@ 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, verticalListSortingStrategy } from "@dnd-kit/sortable"; +import { restrictToVerticalAxis } from "@dnd-kit/modifiers"; +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 +68,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 +94,30 @@ const MinHorizontalWidthContext = createContext({ minHorizontalWidth: '100px', }); -const ContainerInListView = (props: ContainerBaseProps ) => { +const ContainerInListView = (props: ContainerBaseProps & {itemIdx: number} ) => { const { horizontalWidth, minHorizontalWidth } = useContext(MinHorizontalWidthContext); + const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ + id: String(props.itemIdx), + }); + return (
+ {} + - ); @@ -190,6 +223,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 +247,11 @@ export function ListView(props: Props) { total, }; }, [children.pagination, totalCount]); + + useEffect(() => { + children.listData.dispatchChangeValueAction(data); + }, [JSON.stringify(data)]); + const style = children.style.getView(); const animationStyle = children.animationStyle.getView(); @@ -229,6 +268,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 +290,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 +299,7 @@ export function ListView(props: Props) { deferAction(ContextContainerComp.batchDeleteAction([String(itemIdx)])) ); }; + return (
); + return render; }); @@ -289,6 +331,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 +365,15 @@ export function ListView(props: Props) { $isGrid={noOfColumns > 1} $autoHeight={autoHeight} > - {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..f23edafe4b 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"), @@ -116,7 +117,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)) From d6e478119edc4615609ab3a85c845ecb621c160c Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Wed, 16 Apr 2025 01:21:29 +0500 Subject: [PATCH 2/2] added switch to enable/disable sorting --- .../src/comps/comps/listViewComp/listView.tsx | 52 ++++++++++++++----- .../comps/comps/listViewComp/listViewComp.tsx | 1 + .../listViewComp/listViewPropertyView.tsx | 3 ++ .../packages/lowcoder/src/i18n/locales/en.ts | 3 +- 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx b/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx index a51175b049..826ff0e383 100644 --- a/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx +++ b/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx @@ -23,8 +23,7 @@ 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, verticalListSortingStrategy } from "@dnd-kit/sortable"; -import { restrictToVerticalAxis } from "@dnd-kit/modifiers"; +import { SortableContext, useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { JSONObject } from "@lowcoder-ee/index.sdk"; @@ -94,7 +93,7 @@ const MinHorizontalWidthContext = createContext({ minHorizontalWidth: '100px', }); -const ContainerInListView = (props: ContainerBaseProps & {itemIdx: number} ) => { +const ContainerInListView = (props: ContainerBaseProps & {itemIdx: number, enableSorting?: boolean} ) => { const { horizontalWidth, minHorizontalWidth @@ -104,6 +103,24 @@ const ContainerInListView = (props: ContainerBaseProps & {itemIdx: number} ) => id: String(props.itemIdx), }); + if (!props.enableSorting) { + return ( +
+ +
+ ) + } + return (
void; minHorizontalWidth?: string; horizontalWidth: string; + enableSorting?: boolean; }; function ListItem({ @@ -154,6 +172,7 @@ function ListItem({ scrollContainerRef, minHeight, horizontalGridCells, + enableSorting, } = props; // disable the unmount function to save user's state with pagination @@ -195,6 +214,7 @@ function ListItem({ overflow={"hidden"} minHeight={minHeight} enableGridLines={true} + enableSorting={enableSorting} /> ); @@ -248,6 +268,8 @@ export function ListView(props: Props) { }; }, [children.pagination, totalCount]); + const enableSorting = useMemo(() => children.enableSorting.getView(), [children.enableSorting]); + useEffect(() => { children.listData.dispatchChangeValueAction(data); }, [JSON.stringify(data)]); @@ -313,6 +335,7 @@ export function ListView(props: Props) { unMountFn={unMountFn} horizontalWidth={`${100 / noOfColumns}%`} minHorizontalWidth={horizontal ? minHorizontalWidth : undefined} + enableSorting={enableSorting} /> ); })} @@ -365,15 +388,20 @@ export function ListView(props: Props) { $isGrid={noOfColumns > 1} $autoHeight={autoHeight} > - - String(colIdx)) - } - > - {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 f23edafe4b..00f6807cf8 100644 --- a/client/packages/lowcoder/src/comps/comps/listViewComp/listViewComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/listViewComp/listViewComp.tsx @@ -61,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, () => <>) 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 735b797d4e..acc6488669 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