mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 18:15:21 +03:00
Hide placeholders (#9246)
* New menuVisible logic * Hide placeholders unless sole selected node, required argument, or requested by dynamic config
This commit is contained in:
parent
f02213ae2b
commit
b0842feea2
@ -60,6 +60,10 @@ test('Selection widgets in Data.read node', async ({ page }) => {
|
||||
// Check initially visible arguments
|
||||
const node = locate.graphNodeByBinding(page, 'data')
|
||||
const argumentNames = node.locator('.WidgetArgumentName')
|
||||
await expect(argumentNames).toHaveCount(1)
|
||||
|
||||
// Check arguments after selecting node
|
||||
await node.click()
|
||||
await expect(argumentNames).toHaveCount(3)
|
||||
|
||||
// Set value on `on_problems` (static drop-down)
|
||||
@ -129,6 +133,10 @@ test('Managing aggregates in `aggregate` node', async ({ page }) => {
|
||||
// Check initially visible arguments
|
||||
const node = locate.graphNodeByBinding(page, 'aggregated')
|
||||
const argumentNames = node.locator('.WidgetArgumentName')
|
||||
await expect(argumentNames).toHaveCount(1)
|
||||
|
||||
// Check arguments after selecting node
|
||||
await node.click()
|
||||
await expect(argumentNames).toHaveCount(3)
|
||||
|
||||
// Add first aggregate
|
||||
|
@ -30,6 +30,7 @@ export const graphSelection: GraphSelection = {
|
||||
hoveredNode: undefined,
|
||||
hoveredPort: undefined,
|
||||
isSelected: () => false,
|
||||
isChanging: false,
|
||||
mouseHandler: () => false,
|
||||
selectAll: () => {},
|
||||
selected: new Set(),
|
||||
|
@ -88,14 +88,6 @@ const contentNode = ref<HTMLElement>()
|
||||
const nodeSize = useResizeObserver(rootNode)
|
||||
const baseNodeSize = computed(() => new Vec2(contentNode.value?.scrollWidth ?? 0, nodeSize.value.y))
|
||||
|
||||
/// Menu can be full, partial or off
|
||||
enum MenuState {
|
||||
Full,
|
||||
Partial,
|
||||
Off,
|
||||
}
|
||||
const menuVisible = ref(MenuState.Off)
|
||||
|
||||
const error = computed(() => {
|
||||
const externalId = graph.db.idToExternal(nodeId.value)
|
||||
if (!externalId) return
|
||||
@ -122,12 +114,19 @@ const warning = computed(() => {
|
||||
})
|
||||
|
||||
const isSelected = computed(() => nodeSelection?.isSelected(nodeId.value) ?? false)
|
||||
const isOnlyOneSelected = computed(() => isSelected.value && nodeSelection?.selected.size === 1)
|
||||
watch(isSelected, (selected) => {
|
||||
if (!selected) {
|
||||
menuVisible.value = MenuState.Off
|
||||
}
|
||||
const isOnlyOneSelected = computed(
|
||||
() => isSelected.value && nodeSelection?.selected.size === 1 && !nodeSelection.isChanging,
|
||||
)
|
||||
|
||||
const menuVisible = isOnlyOneSelected
|
||||
const menuFull = ref(false)
|
||||
watch(menuVisible, (visible) => {
|
||||
if (!visible) menuFull.value = false
|
||||
})
|
||||
function openFullMenu() {
|
||||
menuFull.value = true
|
||||
nodeSelection?.setSelection(new Set([nodeId.value]))
|
||||
}
|
||||
|
||||
const isDocsVisible = ref(false)
|
||||
const visualizationWidth = computed(() => props.node.vis?.width ?? null)
|
||||
@ -181,7 +180,6 @@ const dragPointer = usePointer((pos, event, type) => {
|
||||
) {
|
||||
nodeSelection?.handleSelectionOf(event, new Set([nodeId.value]))
|
||||
handleNodeClick(event)
|
||||
menuVisible.value = MenuState.Partial
|
||||
}
|
||||
startEvent = null
|
||||
startEpochMs.value = 0
|
||||
@ -363,13 +361,6 @@ function portGroupStyle(port: PortData) {
|
||||
}
|
||||
}
|
||||
|
||||
function openFullMenu() {
|
||||
if (!nodeSelection?.isSelected(nodeId.value)) {
|
||||
nodeSelection?.setSelection(new Set([nodeId.value]))
|
||||
}
|
||||
menuVisible.value = MenuState.Full
|
||||
}
|
||||
|
||||
const editingComment = ref(false)
|
||||
|
||||
const documentation = computed<string | undefined>({
|
||||
@ -419,12 +410,12 @@ const documentation = computed<string | undefined>({
|
||||
{{ node.pattern?.code() ?? '' }}
|
||||
</div>
|
||||
<CircularMenu
|
||||
v-if="menuVisible === MenuState.Full || menuVisible === MenuState.Partial"
|
||||
v-if="menuVisible"
|
||||
v-model:isOutputContextOverridden="isOutputContextOverridden"
|
||||
v-model:isDocsVisible="isDocsVisible"
|
||||
:isOutputContextEnabledGlobally="projectStore.isOutputContextEnabled"
|
||||
:isVisualizationVisible="isVisualizationVisible"
|
||||
:isFullMenuVisible="menuVisible === MenuState.Full"
|
||||
:isFullMenuVisible="menuVisible && menuFull"
|
||||
@update:isVisualizationVisible="emit('update:visualizationVisible', $event)"
|
||||
@startEditing="startEditingNode"
|
||||
@startEditingComment="editingComment = true"
|
||||
@ -436,7 +427,7 @@ const documentation = computed<string | undefined>({
|
||||
:nodeSize="baseNodeSize"
|
||||
:scale="navigator?.scale ?? 1"
|
||||
:nodePosition="props.node.position"
|
||||
:isCircularMenuVisible="menuVisible === MenuState.Full || menuVisible === MenuState.Partial"
|
||||
:isCircularMenuVisible="menuVisible"
|
||||
:currentType="node.vis?.identifier"
|
||||
:isFullscreen="isVisualizationFullscreen"
|
||||
:dataSource="{ type: 'node', nodeId: externalId }"
|
||||
@ -474,6 +465,7 @@ const documentation = computed<string | undefined>({
|
||||
:icon="icon"
|
||||
:connectedSelfArgumentId="connectedSelfArgumentId"
|
||||
:potentialSelfArgumentId="potentialSelfArgumentId"
|
||||
:extended="isOnlyOneSelected"
|
||||
@openFullMenu="openFullMenu"
|
||||
/>
|
||||
</div>
|
||||
@ -484,7 +476,7 @@ const documentation = computed<string | undefined>({
|
||||
<GraphNodeError
|
||||
v-if="warning && (nodeHovered || isSelected)"
|
||||
class="afterNode warning"
|
||||
:class="{ messageWithMenu: menuVisible !== MenuState.Off }"
|
||||
:class="{ messageWithMenu: menuVisible }"
|
||||
:message="warning"
|
||||
icon="warning"
|
||||
type="warning"
|
||||
|
@ -15,6 +15,7 @@ const props = defineProps<{
|
||||
icon: Icon
|
||||
connectedSelfArgumentId: Ast.AstId | undefined
|
||||
potentialSelfArgumentId: Ast.AstId | undefined
|
||||
extended: boolean
|
||||
}>()
|
||||
const emit = defineEmits<{
|
||||
openFullMenu: []
|
||||
@ -69,6 +70,7 @@ provideWidgetTree(
|
||||
toRef(props, 'icon'),
|
||||
toRef(props, 'connectedSelfArgumentId'),
|
||||
toRef(props, 'potentialSelfArgumentId'),
|
||||
toRef(props, 'extended'),
|
||||
layoutTransitions.active,
|
||||
() => {
|
||||
emit('openFullMenu')
|
||||
|
@ -1,11 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import NodeWidget from '@/components/GraphEditor/NodeWidget.vue'
|
||||
import { WidgetInput, defineWidget, widgetProps } from '@/providers/widgetRegistry'
|
||||
import { injectWidgetTree } from '@/providers/widgetTree'
|
||||
import { Ast } from '@/util/ast'
|
||||
import { ArgumentApplication, ArgumentApplicationKey } from '@/util/callTree'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps(widgetProps(widgetDefinition))
|
||||
const tree = injectWidgetTree()
|
||||
const application = computed(() => props.input[ArgumentApplicationKey])
|
||||
const targetMaybePort = computed(() => {
|
||||
const target = application.value.target
|
||||
@ -45,7 +47,13 @@ export const widgetDefinition = defineWidget(ArgumentApplicationKey, {
|
||||
<div v-if="application.infixOperator" class="infixOp" :style="operatorStyle">
|
||||
<NodeWidget :input="WidgetInput.FromAst(application.infixOperator)" />
|
||||
</div>
|
||||
<NodeWidget :input="application.argument.toWidgetInput()" nest />
|
||||
<div
|
||||
v-if="tree.extended || !application.argument.hideByDefault"
|
||||
class="argument"
|
||||
:class="{ animateWhenShown: application.argument.hideByDefault }"
|
||||
>
|
||||
<NodeWidget :input="application.argument.toWidgetInput()" nest />
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@ -75,4 +83,22 @@ export const widgetDefinition = defineWidget(ArgumentApplicationKey, {
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
|
||||
.argument {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.animateWhenShown {
|
||||
animation: show 4800ms 100ms cubic-bezier(0.38, 0.97, 0.56, 0.76) forwards;
|
||||
max-width: 0;
|
||||
overflow-x: clip;
|
||||
}
|
||||
|
||||
@keyframes show {
|
||||
100% {
|
||||
max-width: 2000px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -148,6 +148,7 @@ export function useSelection<T>(
|
||||
},
|
||||
deselectAll: () => selected.clear(),
|
||||
isSelected: (element: T) => selected.has(element),
|
||||
isChanging: computed(() => anchor.value != null),
|
||||
setSelection,
|
||||
handleSelectionOf,
|
||||
hoveredNode,
|
||||
|
@ -14,6 +14,7 @@ const { provideFn, injectFn } = createContextStore(
|
||||
icon: Ref<Icon>,
|
||||
connectedSelfArgumentId: Ref<Ast.AstId | undefined>,
|
||||
potentialSelfArgumentId: Ref<Ast.AstId | undefined>,
|
||||
extended: Ref<boolean>,
|
||||
hasActiveAnimations: Ref<boolean>,
|
||||
emitOpenFullMenu: () => void,
|
||||
) => {
|
||||
@ -25,6 +26,7 @@ const { provideFn, injectFn } = createContextStore(
|
||||
icon,
|
||||
connectedSelfArgumentId,
|
||||
potentialSelfArgumentId,
|
||||
extended,
|
||||
nodeSpanStart,
|
||||
hasActiveAnimations,
|
||||
emitOpenFullMenu,
|
||||
|
@ -2,6 +2,7 @@ import type { PortId } from '@/providers/portInfo'
|
||||
import { WidgetInput } from '@/providers/widgetRegistry'
|
||||
import type { WidgetConfiguration } from '@/providers/widgetRegistry/configuration'
|
||||
import * as widgetCfg from '@/providers/widgetRegistry/configuration'
|
||||
import { DisplayMode } from '@/providers/widgetRegistry/configuration'
|
||||
import type { SuggestionEntry, SuggestionEntryArgument } from '@/stores/suggestionDatabase/entry'
|
||||
import { Ast } from '@/util/ast'
|
||||
import { findLastIndex, tryGetIndex } from '@/util/data/array'
|
||||
@ -23,7 +24,7 @@ export class ArgumentPlaceholder {
|
||||
public argInfo: SuggestionEntryArgument,
|
||||
public kind: ApplicationKind,
|
||||
public insertAsNamed: boolean,
|
||||
public dynamicConfig?: WidgetConfiguration | undefined,
|
||||
public dynamicConfig?: (WidgetConfiguration & { display?: DisplayMode }) | undefined,
|
||||
) {}
|
||||
|
||||
static WithRetrievedConfig(
|
||||
@ -52,6 +53,10 @@ export class ArgumentPlaceholder {
|
||||
get portId(): PortId {
|
||||
return `${this.callId}[${this.index}]` as PortId
|
||||
}
|
||||
|
||||
get hideByDefault(): boolean {
|
||||
return this.argInfo.hasDefault && this.dynamicConfig?.display !== DisplayMode.Always
|
||||
}
|
||||
}
|
||||
|
||||
export class ArgumentAst {
|
||||
@ -88,6 +93,10 @@ export class ArgumentAst {
|
||||
get portId(): PortId {
|
||||
return this.ast.id
|
||||
}
|
||||
|
||||
get hideByDefault(): boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
type InterpretedCall = InterpretedInfix | InterpretedPrefix
|
||||
|
Loading…
Reference in New Issue
Block a user