diff --git a/frontend/src/lib/lemon-ui/VerticalNestedDND/VerticalNestedDND.tsx b/frontend/src/lib/lemon-ui/VerticalNestedDND/VerticalNestedDND.tsx index c4093bde31d..735032793b6 100644 --- a/frontend/src/lib/lemon-ui/VerticalNestedDND/VerticalNestedDND.tsx +++ b/frontend/src/lib/lemon-ui/VerticalNestedDND/VerticalNestedDND.tsx @@ -30,7 +30,9 @@ import type { Transform } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities' import { IconBuilding, IconTrash } from '@posthog/icons' import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' -import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react' +import debounce from 'lodash.debounce' +import isEqual from 'lodash.isequal' +import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createPortal, unstable_batchedUpdates } from 'react-dom' export interface VDNDChildItem { id: UniqueIdentifier @@ -76,10 +78,18 @@ export function VerticalNestedDND (onChange ? debounce(onChange, 200, { trailing: true }) : undefined), + [onChange] + ) + const savedChanges = useRef(initialItems) useEffect(() => { const newItemsArray = containers.map((containerId) => items[containerId]) - onChange?.(newItemsArray) - }, [containers, items, onChange]) + if (!isEqual(newItemsArray, savedChanges.current)) { + savedChanges.current = newItemsArray + debouncedOnChanged?.(newItemsArray) + } + }, [containers, items, debouncedOnChanged]) const collisionDetectionStrategy: CollisionDetection = useCallback( (args) => { diff --git a/package.json b/package.json index 80b462e6bc8..a03d4986f7e 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,8 @@ "@tiptap/react": "^2.1.16", "@tiptap/starter-kit": "^2.1.16", "@tiptap/suggestion": "^2.1.16", + "@types/lodash.debounce": "^4.0.9", + "@types/lodash.isequal": "^4.5.8", "@types/md5": "^2.3.0", "@types/react-transition-group": "^4.4.5", "@types/react-virtualized": "^9.21.23", @@ -145,6 +147,8 @@ "kea-test-utils": "^0.2.4", "kea-waitfor": "^0.2.1", "kea-window-values": "^3.0.0", + "lodash.debounce": "^4.0.8", + "lodash.isequal": "^4.5.0", "lodash.merge": "^4.6.2", "lodash.uniqby": "^4.7.0", "maplibre-gl": "^3.5.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9f094b78e6..b272feb3b6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -109,6 +109,12 @@ dependencies: '@tiptap/suggestion': specifier: ^2.1.16 version: 2.1.16(@tiptap/core@2.1.16)(@tiptap/pm@2.1.16) + '@types/lodash.debounce': + specifier: ^4.0.9 + version: 4.0.9 + '@types/lodash.isequal': + specifier: ^4.5.8 + version: 4.5.8 '@types/md5': specifier: ^2.3.0 version: 2.3.2 @@ -256,6 +262,12 @@ dependencies: kea-window-values: specifier: ^3.0.0 version: 3.0.0(kea@3.1.5) + lodash.debounce: + specifier: ^4.0.8 + version: 4.0.8 + lodash.isequal: + specifier: ^4.5.0 + version: 4.5.0 lodash.merge: specifier: ^4.6.2 version: 4.6.2 @@ -395,7 +407,7 @@ dependencies: optionalDependencies: fsevents: specifier: ^2.3.2 - version: 2.3.2 + version: 2.3.3 devDependencies: '@babel/core': @@ -6465,7 +6477,7 @@ packages: '@storybook/preview-api': 7.6.4 '@storybook/theming': 7.6.4(react-dom@18.2.0)(react@18.2.0) '@storybook/types': 7.6.4 - '@types/lodash': 4.14.188 + '@types/lodash': 4.17.13 color-convert: 2.0.1 dequal: 2.0.3 lodash: 4.17.21 @@ -6582,7 +6594,7 @@ packages: '@storybook/client-logger': 7.6.20 '@storybook/core-events': 7.6.20 '@storybook/global': 5.0.0 - qs: 6.13.1 + qs: 6.13.0 telejson: 7.2.0 tiny-invariant: 1.3.3 dev: true @@ -7096,7 +7108,7 @@ packages: dequal: 2.0.3 lodash: 4.17.21 memoizerific: 1.11.3 - qs: 6.13.1 + qs: 6.13.0 synchronous-promise: 2.0.17 ts-dedent: 2.2.0 util-deprecate: 1.0.2 @@ -7256,7 +7268,7 @@ packages: dependencies: '@storybook/client-logger': 7.6.20 memoizerific: 1.11.3 - qs: 6.13.1 + qs: 6.13.0 dev: true /@storybook/router@7.6.4: @@ -8406,21 +8418,32 @@ packages: resolution: {integrity: sha512-PecSzorDGdabF57OBeQO/xFbAkYWo88g4Xvnsx7LRwqLC17I7OoKtA3bQB9uXkY6UkMWCOsA8HSVpaoitscdXw==} dev: false + /@types/lodash.debounce@4.0.9: + resolution: {integrity: sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==} + dependencies: + '@types/lodash': 4.17.13 + dev: false + + /@types/lodash.isequal@4.5.8: + resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} + dependencies: + '@types/lodash': 4.17.13 + dev: false + /@types/lodash.merge@4.6.9: resolution: {integrity: sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==} dependencies: - '@types/lodash': 4.14.188 + '@types/lodash': 4.17.13 dev: true /@types/lodash.uniqby@4.7.9: resolution: {integrity: sha512-rjrXji/seS6BZJRgXrU2h6FqxRVufsbq/HE0Tx0SdgbtlWr2YmD/M64BlYEYYlaMcpZwy32IYVkMfUMYlPuv0w==} dependencies: - '@types/lodash': 4.14.188 + '@types/lodash': 4.17.13 dev: true - /@types/lodash@4.14.188: - resolution: {integrity: sha512-zmEmF5OIM3rb7SbLCFYoQhO4dGt2FRM9AMkxvA3LaADOF1n8in/zGJlWji9fmafLoNyz+FoL6FE0SLtGIArD7w==} - dev: true + /@types/lodash@4.17.13: + resolution: {integrity: sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==} /@types/mapbox__point-geometry@0.1.3: resolution: {integrity: sha512-2W46IOXlu7vC8m3+M5rDqSnuY22GFxxx3xhkoyqyPWrD+eP2iAwNst0A1+umLYjCTJMJTSpiofphn9h9k+Kw+w==} @@ -13145,6 +13168,7 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true + dev: true optional: true /fsevents@2.3.3: @@ -18170,8 +18194,8 @@ packages: side-channel: 1.0.6 dev: true - /qs@6.13.1: - resolution: {integrity: sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==} + /qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} dependencies: side-channel: 1.0.6 @@ -18409,7 +18433,7 @@ packages: react: '>=15' dependencies: react: 18.2.0 - unlayer-types: 1.167.0 + unlayer-types: 1.169.0 dev: false /react-error-boundary@3.1.4(react@18.2.0): @@ -20956,8 +20980,8 @@ packages: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} - /unlayer-types@1.167.0: - resolution: {integrity: sha512-H3Qq6WnC4u8hy2Qt+uueUaJkKdtTwv1V8FV5LwM+ZAD1XSMfySQK/FhpXgAbU+/nxBs8kvlJaK0I6D/64J5zpQ==} + /unlayer-types@1.169.0: + resolution: {integrity: sha512-BXT4C/MzrYIiiEXHOovtquGehX4IXLYKx3qo4SwYGmqmaGMvZLKvt2rDlO+DKUjVnvGHxgre/tAbUl3I5Gnpxg==} dev: false /unpipe@1.0.0: