Skip to content

Commit

Permalink
adds a raw data viewer for advanced users (#2296)
Browse files Browse the repository at this point in the history
* feat(gendo): scaffolding

* feat(gendo): wip

* feat(gendo): wip

* feat(gendo): wip

* feat(gendo): wip

* feat(gendo): wip

* feat(gendo): it's alive

* feat(gendo): wip

* feat(gendo): blobifies responses to make gergo happy

* feat(gendo): ratelimiting + lints

* feat(gendo): prettier fix

* feat(gendo): last fixes

* feat(gendo): clarifications

* feat(gendo): helm base

* feat(dataviewer): raw first pass

* feat(dataviewer): some styling

* feat(dataviewer): objects in new + fixes

* feat(dataviewer): resources support for object cards

* feat(dataviewer): adds selected object support

* feat(dataviewer): removes button from main sidebar, integrates with explorer

* feat(dataviewer): some value handling fixes

* feat(dataviewer): linting fixes

* feat(dataviewer): show raw viewer within Scene Explorer

* feat(dataviewer): Update copy and labels

* feat(dataviewer): Improve row borders and divider

* Apply suggestions from code review

* feat(dataviewer): Make sure arrow doesn't shrink

---------

Co-authored-by: andrewwallacespeckle <139135120+andrewwallacespeckle@users.noreply.github.com>
Co-authored-by: Benjamin Ottensten <benjamin.ottensten@gmail.com>
  • Loading branch information
3 people authored May 30, 2024
1 parent bd7dd40 commit 3c15e88
Show file tree
Hide file tree
Showing 13 changed files with 336 additions and 33 deletions.
11 changes: 3 additions & 8 deletions packages/frontend-2/components/viewer/Controls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,6 @@
<IconFileExplorer class="h-4 w-4 md:h-5 md:w-5" />
</ViewerControlsButtonToggle>

<!-- TODO -->
<!-- <ViewerControlsButtonToggle
:active="activeControl === 'filters'"
@click="toggleActiveControl('filters')"
>
<FunnelIcon class="w-5 h-5" />
</ViewerControlsButtonToggle> -->

<!-- Comment threads -->
<ViewerControlsButtonToggle
v-tippy="isSmallerOrEqualSm ? undefined : discussionsShortcut"
Expand Down Expand Up @@ -201,16 +193,19 @@
</div>
</KeepAlive>
</div>

<div v-show="resourceItems.length !== 0 && activeControl === 'explorer'">
<KeepAlive>
<ViewerExplorer class="pointer-events-auto" @close="activeControl = 'none'" />
</KeepAlive>
</div>

<ViewerComments
v-if="resourceItems.length !== 0 && activeControl === 'discussions'"
class="pointer-events-auto"
@close="activeControl = 'none'"
/>

<div v-show="resourceItems.length !== 0 && activeControl === 'automate'">
<AutomateViewerPanel
:automation-runs="allAutomationRuns"
Expand Down
61 changes: 61 additions & 0 deletions packages/frontend-2/components/viewer/dataviewer/Object.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<template>
<div>
<CommonLoadingBar v-show="loading" :loading="loading" />
<div class="space-y-1">
<div v-for="kvp in limitedKvps" :key="kvp.key">
<ViewerDataviewerRow :prop="kvp" />
</div>
<div v-if="limit < kvps.length">
<FormButton text full-width size="xs" @click="limit += 20">
show more
</FormButton>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { CommonLoadingBar } from '@speckle/ui-components'
import { useLazyQuery } from '@vue/apollo-composable'
import { useInjectedViewerState } from '~/lib/viewer/composables/setup'
import { viewerRawObjectQuery } from '~/lib/viewer/graphql/queries'
const { projectId } = useInjectedViewerState()
const props = defineProps<{
object: Record<string, unknown>
}>()
const { result, loading, load } = useLazyQuery(viewerRawObjectQuery, () => ({
streamId: projectId.value,
objectId: props.object['referencedId'] as string
}))
if (props.object['referencedId']) {
load()
}
const kvps = computed(() => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const obj = (result.value?.stream?.object?.data || props.object) as Record<
string,
unknown
>
const keys = Object.keys(obj)
const localKvps = []
for (const key of keys) {
// if (!obj[key]) continue // TODO: deal with null/undef
const value = obj[key] || obj[key] === 0 ? obj[key] : 'null'
localKvps.push({
key,
value,
type: Array.isArray(value) ? 'array' : typeof value
})
}
return localKvps
})
const limit = ref(20)
const limitedKvps = computed(() => {
return kvps.value.slice(0, limit.value)
})
</script>
43 changes: 43 additions & 0 deletions packages/frontend-2/components/viewer/dataviewer/Panel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<template>
<div class="px-2 divide-y divide-dashed divide-outline-3">
<div v-for="obj in rootObjs" :key="obj.referencedId" class="py-3">
<div class="font-bold text-xs pl-1 mb-2 text-foreground-1">
{{ obj.name }}
</div>
<ViewerDataviewerObject :object="obj" />
</div>
</div>
</template>
<script setup lang="ts">
import { useSelectionUtilities } from '~/lib/viewer/composables/ui'
import { useInjectedViewerLoadedResources } from '~~/lib/viewer/composables/setup'
const { modelsAndVersionIds, objects } = useInjectedViewerLoadedResources()
const { objects: selectedObjects } = useSelectionUtilities()
const rootObjs = computed(() => {
const selection = selectedObjects.value.map((o) => ({
referencedId: o.id,
name: 'Selection',
// eslint-disable-next-line camelcase
speckle_type: 'reference'
}))
const models = modelsAndVersionIds.value.map((m) => ({
referencedId: m.model.loadedVersion.items[0].referencedObject,
name: m.model.name,
// eslint-disable-next-line camelcase
speckle_type: 'reference'
}))
const objs = objects.value.map((m) => ({
referencedId: m.objectId,
name: 'Object ' + m.objectId,
// eslint-disable-next-line camelcase
speckle_type: 'reference'
}))
return [...selection, ...models, ...objs]
})
</script>
64 changes: 64 additions & 0 deletions packages/frontend-2/components/viewer/dataviewer/Row.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<template>
<div
:class="`w-full bg-foundation-2 hover:bg-blue-500/5 rounded px-1 py-1 border-l-2 text-xs ${
expandable ? 'border-blue-500' : 'border-transparent'
} ${expanded ? 'border-neutral-500 border-opacity-30' : ''}`"
>
<!-- eslint-disable-next-line vuejs-accessibility/click-events-have-key-events -->
<div
:class="`grid grid-cols-3 ${expandable ? 'cursor-pointer' : ''}`"
@click="handleExpand"
>
<div class="col-span-1 mr-1 flex items-center text-foreground-2 font-semibold">
<ChevronRightIcon
v-if="expandable"
:class="`w-3 ${expanded ? 'rotate-90' : ''} transition shrink-0 `"
/>
<span class="select-all truncate">{{ prop.key }}</span>
</div>
<div v-if="!expandable" class="col-span-2 truncate select-all">
{{ prop.value }}
</div>
<div v-if="expandable" class="col-span-2 truncate">
{{ prop.type }}
<span v-if="prop.type === 'array'" class="text-foreground-2 text-xs">
({{ arrayLen }})
</span>
</div>
</div>
<div v-if="expandable && expanded" class="w-full pl-1 pt-2">
<ViewerDataviewerObject :object="castProp" />
</div>
</div>
</template>
<script setup lang="ts">
import { ChevronRightIcon } from '@heroicons/vue/20/solid'
const props = defineProps<{
prop: {
key: string
value: unknown
type: string
}
}>()
const expanded = ref(false)
const expandable = computed(() => {
return props.prop.type === 'array' || props.prop.type === 'object'
})
const arrayLen = computed(() => {
if (props.prop.type !== 'array') return
const arr = props.prop.value as unknown[]
return arr.length
})
const castProp = computed(() => {
return props.prop.value as Record<string, unknown>
})
const handleExpand = () => {
if (!expandable.value) return
expanded.value = !expanded.value
}
</script>
69 changes: 50 additions & 19 deletions packages/frontend-2/components/viewer/explorer/Explorer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,49 @@
<template #title>Scene Explorer</template>

<template #actions>
<FormButton
size="xs"
text
:icon-left="BarsArrowDownIcon"
@click="expandLevel++"
>
Unfold
</FormButton>
<FormButton
size="xs"
text
:icon-left="BarsArrowUpIcon"
:disabled="expandLevel <= -1 && manualExpandLevel <= -1"
@click="collapse()"
>
Collapse
</FormButton>
<div class="flex items-center justify-between w-full">
<div v-if="!showRaw" class="flex items-center">
<FormButton
size="xs"
text
:icon-left="BarsArrowDownIcon"
@click="expandLevel++"
>
Unfold
</FormButton>
<FormButton
size="xs"
text
:icon-left="BarsArrowUpIcon"
:disabled="expandLevel <= -1 && manualExpandLevel <= -1"
@click="collapse()"
>
Collapse
</FormButton>
</div>
<div v-else>
<h4 class="font-bold whitespace-normal text-xs ml-1">Dev Mode</h4>
</div>

<FormButton
v-tippy="showRaw ? 'Switch back' : 'Switch to Dev Mode'"
size="xs"
text
class="-mr-0.5 sm:-mr-1"
color="secondary"
@click="showRaw = !showRaw"
>
<CodeBracketIcon
class="size-4 sm:size-3"
:class="showRaw ? 'text-primary' : 'text-foreground'"
/>
</FormButton>
</div>
</template>
<div v-if="rootNodes.length !== 0" class="relative flex flex-col space-y-2 py-2">
<div
v-if="!showRaw && rootNodes.length !== 0"
class="relative flex flex-col gap-y-2 py-2"
>
<div
v-for="(rootNode, idx) in rootNodes"
:key="idx"
Expand All @@ -39,12 +63,17 @@
/>
</div>
</div>
<ViewerDataviewerPanel v-if="showRaw" class="pointer-events-auto" />
</ViewerLayoutPanel>
<ViewerExplorerFilters :filters="allFilters || []" />
</div>
</template>
<script setup lang="ts">
import { BarsArrowUpIcon, BarsArrowDownIcon } from '@heroicons/vue/24/solid'
import {
BarsArrowUpIcon,
BarsArrowDownIcon,
CodeBracketIcon
} from '@heroicons/vue/24/solid'
import { ViewerEvent } from '@speckle/viewer'
import type { ExplorerNode } from '~~/lib/common/helpers/sceneExplorer'
import {
Expand Down Expand Up @@ -75,6 +104,8 @@ const collapse = () => {
if (manualExpandLevel.value > -1) manualExpandLevel.value--
}
const showRaw = ref(false)
// TODO: worldTree being set in postSetup.ts (viewer) does not seem to create a reactive effect
// in here (as i was expecting it to?). Therefore, refHack++ to trigger the computed prop rootNodes.
// Possibly Fabs will know more :)
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend-2/components/viewer/layout/Panel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
class="absolute top-1.5 sm:top-2 right-0.5 sm:right-0 z-10"
>
<FormButton size="sm" color="secondary" text @click="$emit('close')">
<XMarkIcon class="h-4 w-4 sm:h-3 sm:w-3 text-primary sm:text-foreground" />
<XMarkIcon class="size-4 sm:size-3 text-primary sm:text-foreground" />
</FormButton>
</div>
<div
Expand Down
19 changes: 16 additions & 3 deletions packages/frontend-2/components/viewer/resources/List.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
size="xs"
text
:icon-left="showRemove ? CheckIcon : MinusIcon"
:disabled="modelsAndVersionIds.length <= 1"
:disabled="!removeEnabled"
@click="showRemove = !showRemove"
>
{{ showRemove ? 'Done' : 'Remove' }}
Expand All @@ -35,6 +35,15 @@
@remove="(id:string) => removeModel(id)"
/>
</div>
<template v-if="objects.length !== 0">
<ViewerResourcesObjectCard
v-for="object in objects"
:key="object.objectId"
:object="object"
:show-remove="showRemove"
@remove="(id:string) => removeModel(id)"
/>
</template>
</template>
</div>
<ViewerResourcesAddModelDialog v-model:open="open" />
Expand All @@ -52,7 +61,8 @@ import { useMixpanel } from '~~/lib/core/composables/mp'
defineEmits(['close'])
const showRemove = ref(false)
const { resourceItems, modelsAndVersionIds } = useInjectedViewerLoadedResources()
const { resourceItems, modelsAndVersionIds, objects } =
useInjectedViewerLoadedResources()
const { items } = useInjectedViewerRequestedResources()
const open = ref(false)
Expand All @@ -68,7 +78,8 @@ const removeModel = async (modelId: string) => {
builder.addModel(loadedResource.modelId, loadedResource.versionId || undefined)
}
} else {
builder.addObject(loadedResource.objectId)
if (loadedResource.objectId !== modelId)
builder.addObject(loadedResource.objectId)
}
}
mp.track('Viewer Action', { type: 'action', name: 'federation', action: 'remove' })
Expand All @@ -78,4 +89,6 @@ const removeModel = async (modelId: string) => {
watch(modelsAndVersionIds, (newVal) => {
if (newVal.length <= 1) showRemove.value = false
})
const removeEnabled = computed(() => items.value.length > 1)
</script>
Loading

0 comments on commit 3c15e88

Please sign in to comment.