Skip to content

Commit

Permalink
Merge pull request #623 from liam-hq/browser_back_and_forward_for_hid…
Browse files Browse the repository at this point in the history
…den_nodes

✨ Add support for hidden nodes in URL state management
  • Loading branch information
FunamaYukina authored Jan 30, 2025
2 parents 8c295ca + 01b027f commit f13710d
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 51 deletions.
6 changes: 6 additions & 0 deletions .changeset/famous-clouds-knock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@liam-hq/erd-core": patch
"@liam-hq/cli": patch
---

✨ Add support for hidden nodes in URL state management
Original file line number Diff line number Diff line change
@@ -1,50 +1,19 @@
import type { QueryParam } from '@/schemas/queryParam'
import { type ShowMode, showModeSchema } from '@/schemas/showMode'
import {
addHiddenNodeIds,
updateActiveTableName,
updateShowMode,
} from '@/stores'
import { decompressFromEncodedURIComponent } from '@/utils'
import {
getActiveTableNameFromUrl,
getHiddenNodeIdsFromUrl,
getShowModeFromUrl,
} from '@/utils'
import { type Node, useReactFlow } from '@xyflow/react'
import { useEffect, useMemo } from 'react'
import * as v from 'valibot'
import { useERDContentContext } from './ERDContentContext'
import { highlightNodesAndEdges } from './highlightNodesAndEdges'
import { useAutoLayout } from './useAutoLayout'

const getActiveTableNameFromUrl = (): string | undefined => {
const urlParams = new URLSearchParams(window.location.search)
const activeQueryParam: QueryParam = 'active'
const tableName = urlParams.get(activeQueryParam)

return tableName || undefined
}

const getHiddenNodeIdsFromUrl = async (): Promise<string[]> => {
const urlParams = new URLSearchParams(window.location.search)
const hiddenQueryParam: QueryParam = 'hidden'
const compressed = urlParams.get(hiddenQueryParam)
const hiddenNodeIds = compressed
? await decompressFromEncodedURIComponent(compressed).catch(() => undefined)
: undefined

return hiddenNodeIds ? hiddenNodeIds.split(',') : []
}

const getShowModeFromUrl = (): ShowMode => {
const urlParams = new URLSearchParams(window.location.search)
const showModeQueryParam: QueryParam = 'showMode'
const showMode = urlParams.get(showModeQueryParam)

const result = v.safeParse(showModeSchema, showMode)
if (result.success && result.output) {
return result.output
}

return 'TABLE_NAME'
}

export const useInitialAutoLayout = (
nodes: Node[],
shouldFitViewToActiveTable: boolean,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
import { showModeSchema } from '@/schemas/showMode'
import {
replaceHiddenNodeIds,
updateActiveTableName,
updateIsPopstateInProgress,
updateShowMode,
} from '@/stores'
import {
getActiveTableNameFromUrl,
getHiddenNodeIdsFromUrl,
getShowModeFromUrl,
} from '@/utils'
import { useEffect } from 'react'
import * as v from 'valibot'

export const usePopStateListener = () => {
useEffect(() => {
const handlePopState = async () => {
const url = new URL(window.location.href)
const tableName = url.searchParams.get('active')
const showMode = url.searchParams.get('showMode')
const tableName = getActiveTableNameFromUrl()
const showMode = getShowModeFromUrl()
const hiddenNodeIds = await getHiddenNodeIdsFromUrl()

updateIsPopstateInProgress(true)
updateActiveTableName(tableName ?? undefined)

const result = v.safeParse(showModeSchema, showMode)
if (result.success && result.output) {
updateShowMode(result.output)
}
await new Promise<void>((resolve) => {
updateActiveTableName(tableName ?? undefined)
updateShowMode(showMode)
setTimeout(resolve, 1)
})

await new Promise<void>((resolve) => {
replaceHiddenNodeIds(hiddenNodeIds)
setTimeout(resolve, 1)
})

setTimeout(() => {
updateIsPopstateInProgress(false)
}, 0)
updateIsPopstateInProgress(false)
}

window.addEventListener('popstate', handlePopState)
Expand Down
7 changes: 5 additions & 2 deletions frontend/packages/erd-core/src/stores/userEditing/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,17 @@ subscribe(userEditingStore.hiddenNodeIds, async () => {
const url = new URL(window.location.href)
const activeQueryParam: QueryParam = 'hidden'
const hiddenNodeIds = Array.from(userEditingStore.hiddenNodeIds).join(',')

const isPopstateInProgress = userEditingStore.isPopstateInProgress
url.searchParams.delete(activeQueryParam)

if (hiddenNodeIds) {
const compressed = await compressToEncodedURIComponent(hiddenNodeIds)
url.searchParams.set(activeQueryParam, compressed)
}

window.history.pushState({}, '', url)
if (!isPopstateInProgress) {
window.history.pushState({}, '', url)
}
})

subscribeKey(userEditingStore, 'showMode', (newShowMode) => {
Expand Down
1 change: 1 addition & 0 deletions frontend/packages/erd-core/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './compressionString'
export * from './urlParams'
33 changes: 33 additions & 0 deletions frontend/packages/erd-core/src/utils/urlParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { QueryParam } from '@/schemas/queryParam'
import { type ShowMode, showModeSchema } from '@/schemas/showMode'
import { decompressFromEncodedURIComponent } from '@/utils'
import * as v from 'valibot'

const getQueryParam = (param: QueryParam): string | undefined => {
const urlParams = new URLSearchParams(window.location.search)
const value = urlParams.get(param)
return value || undefined
}

export const getActiveTableNameFromUrl = (): string | undefined => {
return getQueryParam('active')
}

export const getHiddenNodeIdsFromUrl = async (): Promise<string[]> => {
const compressed = getQueryParam('hidden')
const decompressed = compressed
? await decompressFromEncodedURIComponent(compressed).catch(() => undefined)
: undefined

return decompressed ? decompressed.split(',') : []
}

export const getShowModeFromUrl = (): ShowMode => {
const showMode = getQueryParam('showMode')
const result = v.safeParse(showModeSchema, showMode)
if (result.success && result.output) {
return result.output
}

return 'TABLE_NAME'
}

0 comments on commit f13710d

Please sign in to comment.