enso/app/gui2/stories/GraphNode.story.vue
Kaz Wesley f88bd90ebb
Refactor for YJs AST (#8840)
Some refactoring separated from #8825 for easier review.

# Important Notes
**ID types**

The new *synchronization IDs* will replace `ExprId` for `Ast` references in frontend logic. `ExprId` (now called `ExternalId`) is now used only for module serialization and engine communication. The graph database will maintain an index that is used to translate at the boundaries. For now, this translation is implemented as a type cast, as the IDs have the same values until the next PR.

- `AstId`: Identifies an `Ast` node.
- `NodeId`: A subtype of `AstId`.
- `ExternalId`: UUID used for serialization and engine communication.

**Other changes**:

- Immediate validation of `Owned` usage.
- Eliminate `Ast.RawCode`.
- Prepare to remove `IdMap` from yjsModel.
2024-01-24 19:22:05 +00:00

100 lines
3.2 KiB
Vue

<script setup lang="ts">
import GraphNode from '@/components/GraphEditor/GraphNode.vue'
import { useNavigator } from '@/composables/navigator'
import { provideGraphSelection } from '@/providers/graphSelection'
import type { Node } from '@/stores/graph'
import { Ast } from '@/util/ast'
import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
import { logEvent } from 'histoire/client'
import { computed, reactive, ref, watchEffect } from 'vue'
import { IdMap, type SourceRange } from '../shared/yjsModel'
import { createSetupComponent } from './histoire/utils'
const idMap = new IdMap()
const nodeBinding = ref('binding')
const nodeContent = ref('content')
const nodeX = ref(0)
const nodeY = ref(0)
const selected = ref(false)
const isLatestSelected = ref(false)
const fullscreenVis = ref(false)
const position = computed(() => new Vec2(nodeX.value, nodeY.value))
function updateContent(updates: [range: SourceRange, content: string][]) {
let content = nodeContent.value
for (const [[start, end], replacement] of updates) {
content = content.slice(0, start) + replacement + content.slice(end)
}
nodeContent.value = content
}
const rootSpan = computed(() => Ast.parseTransitional(nodeContent.value, idMap))
const pattern = computed(() => Ast.parseTransitional(nodeBinding.value, idMap))
const node = computed((): Node => {
return {
outerExprId: '' as any,
pattern: pattern.value,
position: position.value,
rootSpan: rootSpan.value,
vis: undefined,
}
})
const mockRects = reactive(new Map())
watchEffect((onCleanup) => {
const id = node.value.rootSpan.id
mockRects.set(id, Rect.Zero)
onCleanup(() => {
mockRects.delete(id)
})
})
const navigator = useNavigator(ref())
const SetupStory = createSetupComponent((app) => {
const selection = provideGraphSelection._mock([navigator, mockRects], app)
watchEffect(() => {
if (selected.value) {
selection.selectAll()
} else {
selection.deselectAll()
}
})
})
</script>
<template>
<Story title="Node" group="graph" :layout="{ type: 'grid', width: 300 }" autoPropsDisabled>
<SetupStory />
<div style="height: 72px; padding: 20px; padding-left: 50px">
<GraphNode
:edited="false"
:node="node"
@movePosition="
(nodeX += $event.x),
(nodeY += $event.y),
logEvent('movePosition', [JSON.stringify($event)])
"
@update:content="updateContent($event), logEvent('updateContent', [JSON.stringify($event)])"
@update:rect="(rect) => logEvent('update:rect', JSON.stringify(rect))"
@replaceSelection="(selected = true), logEvent('replaceSelection', [])"
@update:selected="(selected = $event), logEvent('update:selected', [$event])"
/>
</div>
<template #controls>
<HstText v-model="nodeBinding" title="node.binding" />
<HstText v-model="nodeContent" title="node.content" />
<HstNumber v-model="nodeX" title="node.position.x" />
<HstNumber v-model="nodeY" title="node.position.y" />
<HstCheckbox v-model="selected" title="selected" />
<HstCheckbox v-model="isLatestSelected" title="isLatestSelected" />
<HstCheckbox v-model="fullscreenVis" title="fullscreenVis" />
</template>
</Story>
</template>