-
Notifications
You must be signed in to change notification settings - Fork 323
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Drop-down filtering (#9399)
Fixes #9058 - the filtering so far is a bit aggressive, but I tune it up in next PR(s). [Screencast from 2024-03-13 15-20-17.webm](https://github.com/enso-org/enso/assets/3919101/112ce65a-a8c6-4818-b8b8-9f493caf9c81) Added new special `WidgetEditHandler,` allowing handling "multi-widget" interactions needed for drop down filtering. # Important Notes * Now when clicking on argument name, the edit is accepted (as normal "outside" click), and then the dropdown is opened again (due to handling click event). I didn't figure out how to handle this case properly, left something least confusing.
- 2025.1.1-nightly.2025.1.4
- 2025.1.1-nightly.2025.1.3
- 2024.6.1-nightly.2024.12.30
- 2024.6.1-nightly.2024.12.29
- 2024.6.1-nightly.2024.12.28
- 2024.6.1-nightly.2024.12.27
- 2024.6.1-nightly.2024.12.26
- 2024.6.1-nightly.2024.12.25
- 2024.6.1-nightly.2024.12.24
- 2024.6.1-nightly.2024.12.23
- 2024.6.1-nightly.2024.12.22
- 2024.6.1-nightly.2024.12.21
- 2024.6.1-nightly.2024.12.20
- 2024.5.2
- 2024.5.1
- 2024.5.1-rc7
- 2024.5.1-rc6
- 2024.5.1-rc5
- 2024.5.1-rc4
- 2024.5.1-rc3
- 2024.5.1-rc2
- 2024.5.1-rc1
- 2024.5.1-nightly.2024.12.16
- 2024.5.1-nightly.2024.12.15
- 2024.5.1-nightly.2024.12.14
- 2024.5.1-nightly.2024.12.13
- 2024.5.1-nightly.2024.12.12
- 2024.5.1-nightly.2024.12.11
- 2024.5.1-nightly.2024.12.10
- 2024.5.1-nightly.2024.12.9
- 2024.5.1-nightly.2024.12.7
- 2024.5.1-nightly.2024.12.6
- 2024.5.1-nightly.2024.12.5
- 2024.5.1-nightly.2024.12.4
- 2024.5.1-nightly.2024.12.3
- 2024.5.1-nightly.2024.11.29
- 2024.5.1-nightly.2024.11.28
- 2024.5.1-nightly.2024.11.27
- 2024.5.1-nightly.2024.11.26.1
- 2024.5.1-nightly.2024.11.26
- 2024.5.1-nightly.2024.11.25
- 2024.5.1-nightly.2024.11.24
- 2024.5.1-nightly.2024.11.23
- 2024.5.1-nightly.2024.11.22
- 2024.5.1-nightly.2024.11.21
- 2024.5.1-nightly.2024.11.20
- 2024.5.1-nightly.2024.11.19
- 2024.5.1-nightly.2024.11.18
- 2024.5.1-nightly.2024.11.17
- 2024.5.1-nightly.2024.11.16
- 2024.5.1-nightly.2024.11.15
- 2024.5.1-nightly.2024.11.14.1
- 2024.5.1-nightly.2024.11.14
- 2024.5.1-nightly.2024.11.13
- 2024.5.1-nightly.2024.11.11
- 2024.5.1-nightly.2024.11.5
- 2024.5.1-nightly.2024.11.4
- 2024.5.1-nightly.2024.11.3
- 2024.5.1-nightly.2024.11.2
- 2024.5.1-nightly.2024.11.1
- 2024.5.1-nightly.2024.10.31
- 2024.5.1-nightly.2024.10.30
- 2024.5.1-nightly.2024.10.29
- 2024.5.1-nightly.2024.10.28
- 2024.5.1-nightly.2024.10.27
- 2024.5.1-nightly.2024.10.26
- 2024.5.1-nightly.2024.10.25
- 2024.5.1-nightly.2024.10.24
- 2024.5.1-nightly.2024.10.23
- 2024.5.1-nightly.2024.10.22
- 2024.5.1-nightly.2024.10.21
- 2024.5.1-nightly.2024.10.20
- 2024.5.1-nightly.2024.10.19
- 2024.5.1-nightly.2024.10.18
- 2024.5.1-nightly.2024.10.17
- 2024.5.1-nightly.2024.10.16
- 2024.5.1-nightly.2024.10.15
- 2024.5.1-nightly.2024.10.14
- 2024.5.1-nightly.2024.10.13
- 2024.5.1-nightly.2024.10.12
- 2024.5.1-nightly.2024.10.11
- 2024.5.1-nightly.2024.10.10
- 2024.5.1-nightly.2024.10.9
- 2024.5.1-nightly.2024.10.8
- 2024.5.1-nightly.2024.10.7
- 2024.5.1-nightly.2024.10.6
- 2024.5.1-nightly.2024.10.5
- 2024.5.1-nightly.2024.10.4
- 2024.5.1-nightly.2024.10.3
- 2024.5.1-nightly.2024.10.2
- 2024.5.1-nightly.2024.10.1
- 2024.5.1-nightly.2024.9.30
- 2024.5.1-nightly.2024.9.29
- 2024.5.1-nightly.2024.9.28
- 2024.5.1-nightly.2024.9.27
- 2024.5.1-nightly.2024.9.26
- 2024.5.1-nightly.2024.9.25
- 2024.5.1-nightly.2024.9.24.1
- 2024.5.1-nightly.2024.9.24
- 2024.5.1-nightly.2024.9.23
- 2024.5.1-nightly.2024.9.22
- 2024.5.1-nightly.2024.9.21
- 2024.5.1-nightly.2024.9.20
- 2024.5.1-nightly.2024.9.18
- 2024.4.2
- 2024.4.2-rc2
- 2024.4.2-rc1
- 2024.4.1
- 2024.4.1-rc8
- 2024.4.1-rc7
- 2024.4.1-rc6
- 2024.4.1-rc5
- 2024.4.1-rc4
- 2024.4.1-rc3
- 2024.4.1-rc2
- 2024.4.1-rc1
- 2024.4.1-nightly.2024.9.17
- 2024.4.1-nightly.2024.9.16
- 2024.4.1-nightly.2024.9.15
- 2024.4.1-nightly.2024.9.14
- 2024.4.1-nightly.2024.9.11
- 2024.4.1-nightly.2024.9.10
- 2024.4.1-nightly.2024.9.7
- 2024.4.1-nightly.2024.9.6
- 2024.4.1-nightly.2024.9.5
- 2024.4.1-nightly.2024.9.4
- 2024.4.1-nightly.2024.9.3
- 2024.4.1-nightly.2024.9.2
- 2024.4.1-nightly.2024.8.31.1
- 2024.4.1-nightly.2024.8.31
- 2024.4.1-nightly.2024.8.30
- 2024.4.1-nightly.2024.8.28
- 2024.4.1-nightly.2024.8.27
- 2024.4.1-nightly.2024.8.26
- 2024.4.1-nightly.2024.8.23
- 2024.4.1-nightly.2024.8.21
- 2024.4.1-nightly.2024.8.20
- 2024.4.1-nightly.2024.8.19
- 2024.4.1-nightly.2024.8.18
- 2024.4.1-nightly.2024.8.17
- 2024.4.1-nightly.2024.8.16
- 2024.4.1-nightly.2024.8.15
- 2024.4.1-nightly.2024.8.14
- 2024.4.1-nightly.2024.8.13
- 2024.4.1-nightly.2024.8.7
- 2024.4.1-nightly.2024.8.6
- 2024.4.1-nightly.2024.8.5
- 2024.4.1-nightly.2024.8.4
- 2024.4.1-nightly.2024.8.2
- 2024.4.1-nightly.2024.8.1
- 2024.3.1
- 2024.3.1-rc5
- 2024.3.1-rc4
- 2024.3.1-rc3
- 2024.3.1-rc2
- 2024.3.1-rc1
- 2024.3.1-nightly.2024.7.31
- 2024.3.1-nightly.2024.7.30
- 2024.3.1-nightly.2024.7.29
- 2024.3.1-nightly.2024.7.28
- 2024.3.1-nightly.2024.7.27
- 2024.3.1-nightly.2024.7.26
- 2024.3.1-nightly.2024.7.25
- 2024.3.1-nightly.2024.7.24
- 2024.3.1-nightly.2024.7.23
- 2024.3.1-nightly.2024.7.22
- 2024.3.1-nightly.2024.7.21
- 2024.3.1-nightly.2024.7.20
- 2024.3.1-nightly.2024.7.19
- 2024.3.1-nightly.2024.7.18
- 2024.3.1-nightly.2024.7.17
- 2024.3.1-nightly.2024.7.16
- 2024.3.1-nightly.2024.7.15
- 2024.3.1-nightly.2024.7.14
- 2024.3.1-nightly.2024.7.13
- 2024.3.1-nightly.2024.7.12.1
- 2024.3.1-nightly.2024.7.12
- 2024.3.1-nightly.2024.7.11.1
- 2024.3.1-nightly.2024.7.11
- 2024.3.1-nightly.2024.7.10
- 2024.3.1-nightly.2024.7.9
- 2024.3.1-nightly.2024.7.8
- 2024.3.1-nightly.2024.7.7
- 2024.3.1-nightly.2024.7.6
- 2024.3.1-nightly.2024.7.5
- 2024.3.1-nightly.2024.7.4
- 2024.3.1-nightly.2024.7.3
- 2024.2.3
- 2024.2.2
- 2024.2.1
- 2024.2.1-rc1
- 2024.2.1-nightly.2024.7.2
- 2024.2.1-nightly.2024.7.1
- 2024.2.1-nightly.2024.6.30
- 2024.2.1-nightly.2024.6.29
- 2024.2.1-nightly.2024.6.28
- 2024.2.1-nightly.2024.6.27
- 2024.2.1-nightly.2024.6.26
- 2024.2.1-nightly.2024.6.25
- 2024.2.1-nightly.2024.6.24
- 2024.2.1-nightly.2024.6.23
- 2024.2.1-nightly.2024.6.22
- 2024.2.1-nightly.2024.6.21
- 2024.2.1-nightly.2024.6.19
- 2024.2.1-nightly.2024.6.18
- 2024.2.1-nightly.2024.6.17
- 2024.2.1-nightly.2024.6.14
- 2024.2.1-nightly.2024.6.13
- 2024.2.1-nightly.2024.6.12
- 2024.2.1-nightly.2024.6.11
- 2024.1.1
- 2024.1.1-rc.2
- 2024.1.1-rc.1
- 2024.1.1-nightly.2024.6.10
- 2024.1.1-nightly.2024.6.9
- 2024.1.1-nightly.2024.6.8
- 2024.1.1-nightly.2024.6.7
- 2024.1.1-nightly.2024.6.6
- 2024.1.1-nightly.2024.6.5
- 2024.1.1-nightly.2024.6.4
- 2024.1.1-nightly.2024.6.3
- 2024.1.1-nightly.2024.6.2
- 2024.1.1-nightly.2024.6.1
- 2024.1.1-nightly.2024.5.31
- 2024.1.1-nightly.2024.5.30
- 2024.1.1-nightly.2024.5.29
- 2024.1.1-nightly.2024.5.28
- 2024.1.1-nightly.2024.5.27
- 2024.1.1-nightly.2024.5.26
- 2024.1.1-nightly.2024.5.25
- 2024.1.1-nightly.2024.5.24
- 2024.1.1-nightly.2024.5.23
- 2024.1.1-nightly.2024.5.22
- 2024.1.1-nightly.2024.5.21
- 2024.1.1-nightly.2024.5.20
- 2024.1.1-nightly.2024.5.19
- 2024.1.1-nightly.2024.5.18
- 2024.1.1-nightly.2024.5.17
- 2024.1.1-nightly.2024.5.16
- 2024.1.1-nightly.2024.5.15
- 2024.1.1-nightly.2024.5.14
- 2024.1.1-nightly.2024.5.11
- 2024.1.1-nightly.2024.5.10
- 2024.1.1-nightly.2024.5.9
- 2024.1.1-nightly.2024.5.8
- 2024.1.1-nightly.2024.5.7.1
- 2024.1.1-nightly.2024.5.7
- 2024.1.1-nightly.2024.5.6
- 2024.1.1-nightly.2024.5.5
- 2024.1.1-nightly.2024.5.4
- 2024.1.1-nightly.2024.5.3
- 2024.1.1-nightly.2024.5.2
- 2024.1.1-nightly.2024.5.1
- 2024.1.1-nightly.2024.4.30
- 2024.1.1-nightly.2024.4.29
- 2024.1.1-nightly.2024.4.26
- 2024.1.1-nightly.2024.4.24
- 2024.1.1-nightly.2024.4.22
- 2024.1.1-nightly.2024.4.20
- 2024.1.1-nightly.2024.4.19
- 2024.1.1-nightly.2024.4.18
- 2024.1.1-nightly.2024.4.17
- 2024.1.1-nightly.2024.4.15
- 2024.1.1-nightly.2024.4.14
- 2024.1.1-nightly.2024.4.13
- 2024.1.1-nightly.2024.4.12
- 2024.1.1-nightly.2024.4.11
- 2024.1.1-nightly.2024.4.10
- 2024.1.1-nightly.2024.4.9
- 2024.1.1-nightly.2024.4.8
- 2024.1.1-nightly.2024.4.7
- 2024.1.1-nightly.2024.4.6
- 2024.1.1-nightly.2024.4.4.1
- 2024.1.1-nightly.2024.4.4
- 2024.1.1-nightly.2024.4.3
- 2024.1.1-nightly.2024.4.2
- 2024.1.1-nightly.2024.4.1
- 2024.1.1-nightly.2024.3.31
- 2024.1.1-nightly.2024.3.30
- 2024.1.1-nightly.2024.3.29
- 2024.1.1-nightly.2024.3.28
- 2024.1.1-nightly.2024.3.27
- 2024.1.1-nightly.2024.3.26
- 2024.1.1-nightly.2024.3.25
- 2024.1.1-nightly.2024.3.24
- 2024.1.1-nightly.2024.3.23
- 2024.1.1-nightly.2024.3.22
- 2024.1.1-nightly.2024.3.20
- 2024.1.1-nightly.2024.3.19
- 2024.1.1-nightly.2024.3.18
- 2024.1.1-nightly.2024.3.16
Showing
10 changed files
with
577 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
app/gui2/src/providers/widgetRegistry/__tests__/editHandler.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import type { GraphNavigator } from '@/providers/graphNavigator' | ||
import { InteractionHandler } from '@/providers/interactionHandler' | ||
import type { PortId } from '@/providers/portInfo' | ||
import { assert } from 'shared/util/assert' | ||
import { expect, test, vi, type Mock } from 'vitest' | ||
import { WidgetEditHandler } from '../editHandler' | ||
|
||
// If widget's name is a prefix of another widget's name, then it is its ancestor. | ||
// The ancestor with longest name is a direct parent. | ||
function editHandlerTree( | ||
widgets: string[], | ||
interactionHandler: InteractionHandler, | ||
createInteraction: (name: string) => Record<string, Mock>, | ||
): Map<string, { handler: WidgetEditHandler; interaction: Record<string, Mock> }> { | ||
const handlers = new Map() | ||
for (const id of widgets) { | ||
let parent: string | undefined | ||
for (const [otherId] of handlers) { | ||
if (id.startsWith(otherId) && otherId.length > (parent?.length ?? -1)) parent = otherId | ||
} | ||
const interaction = createInteraction(id) | ||
const handler = new WidgetEditHandler( | ||
id as PortId, | ||
interaction, | ||
parent ? handlers.get(parent)?.handler : undefined, | ||
interactionHandler, | ||
) | ||
handlers.set(id, { handler, interaction }) | ||
} | ||
return handlers | ||
} | ||
|
||
test.each` | ||
widgets | edited | expectedPropagation | ||
${['A']} | ${'A'} | ${['A']} | ||
${['A', 'A1', 'B']} | ${'A1'} | ${['A', 'A1']} | ||
${['A', 'A1', 'A2']} | ${'A2'} | ${['A', 'A2']} | ||
${['A', 'A1', 'A11']} | ${'A1'} | ${['A', 'A1']} | ||
${['A', 'A1', 'A11']} | ${'A11'} | ${['A', 'A1', 'A11']} | ||
${['A', 'A1', 'A2', 'A21']} | ${'A21'} | ${['A', 'A2', 'A21']} | ||
`( | ||
'Edit interaction propagation starting from $edited in $widgets tree', | ||
({ widgets, edited, expectedPropagation }) => { | ||
const interactionHandler = new InteractionHandler() | ||
const handlers = editHandlerTree(widgets, interactionHandler, () => ({ | ||
start: vi.fn(), | ||
edit: vi.fn(), | ||
end: vi.fn(), | ||
cancel: vi.fn(), | ||
})) | ||
const expectedPropagationSet = new Set(expectedPropagation) | ||
const checkCallbackCall = (callback: string, ...args: any[]) => { | ||
for (const [id, { interaction }] of handlers) { | ||
if (expectedPropagationSet.has(id)) { | ||
expect(interaction[callback]).toHaveBeenCalledWith(...args) | ||
} else { | ||
expect(interaction[callback]).not.toHaveBeenCalled() | ||
} | ||
interaction[callback]?.mockClear() | ||
} | ||
} | ||
|
||
const editedHandler = handlers.get(edited) | ||
assert(editedHandler != null) | ||
|
||
editedHandler.handler.start() | ||
checkCallbackCall('start', edited) | ||
for (const [id, { handler }] of handlers) { | ||
expect(handler.isActive()).toBe(expectedPropagationSet.has(id)) | ||
} | ||
|
||
editedHandler.handler.edit('13') | ||
checkCallbackCall('edit', edited, '13') | ||
|
||
for (const ended of expectedPropagation) { | ||
const endedHandler = handlers.get(ended)?.handler | ||
|
||
editedHandler.handler.start() | ||
expect(editedHandler.handler.isActive()).toBeTruthy() | ||
endedHandler?.end() | ||
checkCallbackCall('end', ended) | ||
expect(editedHandler.handler.isActive()).toBeFalsy() | ||
|
||
editedHandler.handler.start() | ||
expect(editedHandler.handler.isActive()).toBeTruthy() | ||
endedHandler?.cancel() | ||
checkCallbackCall('cancel') | ||
expect(editedHandler.handler.isActive()).toBeFalsy() | ||
} | ||
|
||
editedHandler.handler.start() | ||
expect(editedHandler.handler.isActive()).toBeTruthy() | ||
interactionHandler.setCurrent(undefined) | ||
checkCallbackCall('cancel') | ||
expect(editedHandler.handler.isActive()).toBeFalsy() | ||
}, | ||
) | ||
|
||
test.each` | ||
name | widgets | edited | propagatingHandlers | nonPropagatingHandlers | expectedHandlerCalls | ||
${'Propagating'} | ${['A', 'A1']} | ${'A1'} | ${['A', 'A1']} | ${[]} | ${['A', 'A1']} | ||
${'Parent edited'} | ${['A', 'A1']} | ${'A'} | ${['A', 'A1']} | ${[]} | ${['A']} | ||
${'Not propagating'} | ${['A', 'A1']} | ${'A1'} | ${['A1']} | ${['A']} | ${['A']} | ||
${'Child only'} | ${['A', 'A1']} | ${'A1'} | ${['A1']} | ${[]} | ${['A1']} | ||
${'Skipping handler without click'} | ${['A', 'A1', 'A12']} | ${'A12'} | ${['A', 'A12']} | ${[]} | ${['A', 'A12']} | ||
${'Stopping propagation'} | ${['A', 'A1', 'A12']} | ${'A12'} | ${['A', 'A12']} | ${['A1']} | ${['A', 'A1']} | ||
`( | ||
'Handling clicks in WidgetEditHandlers case $name', | ||
({ widgets, edited, propagatingHandlers, nonPropagatingHandlers, expectedHandlerCalls }) => { | ||
const event = new MouseEvent('click') as PointerEvent | ||
const navigator = {} as GraphNavigator | ||
const interactionHandler = new InteractionHandler() | ||
|
||
const propagatingHandlersSet = new Set(propagatingHandlers) | ||
const nonPropagatingHandlersSet = new Set(nonPropagatingHandlers) | ||
const expectedHandlerCallsSet = new Set(expectedHandlerCalls) | ||
|
||
const handlers = editHandlerTree(widgets, interactionHandler, (id) => | ||
propagatingHandlersSet.has(id) ? | ||
{ | ||
click: vi.fn((e, nav, childHandler) => { | ||
expect(e).toBe(event) | ||
expect(nav).toBe(navigator) | ||
childHandler?.() | ||
}), | ||
} | ||
: nonPropagatingHandlersSet.has(id) ? | ||
{ | ||
click: vi.fn((e, nav) => { | ||
expect(e).toBe(event) | ||
expect(nav).toBe(navigator) | ||
}), | ||
} | ||
: {}, | ||
) | ||
handlers.get(edited)?.handler.start() | ||
interactionHandler.handleClick(event, navigator) | ||
for (const [id, { interaction }] of handlers) { | ||
if (expectedHandlerCallsSet.has(id)) | ||
expect(interaction.click, `${id} click handler`).toHaveBeenCalled() | ||
else if (interaction.click) | ||
expect(interaction.click, `${id} click handler`).not.toHaveBeenCalled() | ||
} | ||
}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import type { PortId } from '@/providers//portInfo' | ||
import type { GraphNavigator } from '@/providers/graphNavigator' | ||
import { | ||
injectInteractionHandler, | ||
type Interaction, | ||
type InteractionHandler, | ||
} from '@/providers/interactionHandler' | ||
import type { WidgetInput } from '@/providers/widgetRegistry' | ||
import type { Ast } from '@/util/ast' | ||
|
||
/** An extend {@link Interaction} used in {@link WidgetEditHandler} */ | ||
export interface WidgetEditInteraction extends Interaction { | ||
/** Click handler from {@link Interaction}, but receives child's click handler. See | ||
* {@link WidgetEditHandler} for details */ | ||
click?( | ||
event: PointerEvent, | ||
navigator: GraphNavigator, | ||
childHandler?: () => boolean | void, | ||
): boolean | void | ||
start?(origin: PortId): void | ||
edit?(origin: PortId, value: Ast.Owned | string): void | ||
end?(origin: PortId): void | ||
} | ||
|
||
/** | ||
* Widget edit handler. | ||
* | ||
* This handler takes an extended interaction and allows cooperation between parent/child | ||
* interactions. A usage example is WidgetSelection, which wants to open when the child is edited | ||
* and filters entries by edited temporary value. | ||
* | ||
* Widget's edit state should be manipulated by `start`, `end` and `cancel` methods; they will set | ||
* proper interaction in the global {@link InteractionHandler} and call the additional callbacks in | ||
* {@link WidgetEditInteraction} passed during construction. | ||
* | ||
* The parent widget may pass its edit handler to one or more children's {@link WidgetInput} to | ||
* bound their interactions; when this child is edited, the parent is also considered edited, | ||
* along with any further ancestors. In particular: | ||
* - Starting, ending and cancelling (including automatic canceling by the global interaction | ||
* handler) of child edit will also call proper callbacks in parent. | ||
* - Cancelling or ending parent edit will cancel/end the child's interaction. | ||
* - `isActive` method of both edit handlers will return true. | ||
* | ||
* This `edited` state is propagated only upwards: if only parent is edited, its children are not | ||
* considered edited. If child starts being edited while parent is still edited, the parent interaction | ||
* will be considered cancelled and then immediately started again. Similarly, when a parent's handler | ||
* is bound to two children, and one of them starts editing while the other is edited, the parent | ||
* will receive `cancel` feedback from the latter and then `start` from the former. | ||
* | ||
* **The `click` handler is a special case:** it will be called only on top-most parent, but its | ||
* handler may decide to delegate it further by calling child's handler passed as an additional | ||
* argument | ||
*/ | ||
export class WidgetEditHandler { | ||
private interaction: WidgetEditInteraction | ||
/** This, or one's child interaction which is currently active */ | ||
private activeInteraction: WidgetEditInteraction | undefined | ||
|
||
constructor( | ||
private portId: PortId, | ||
innerInteraction: WidgetEditInteraction, | ||
private parent?: WidgetEditHandler, | ||
private interactionHandler: InteractionHandler = injectInteractionHandler(), | ||
) { | ||
this.interaction = { | ||
cancel: () => { | ||
this.activeInteraction = undefined | ||
innerInteraction.cancel?.() | ||
parent?.interaction.cancel?.() | ||
}, | ||
click: (event, navigator, childHandler) => { | ||
const innerInteractionClick = innerInteraction.click | ||
const thisHandler = | ||
innerInteractionClick ? | ||
() => innerInteractionClick(event, navigator, childHandler) | ||
: childHandler | ||
if (parent && parent.interaction.click) | ||
return parent.interaction.click(event, navigator, thisHandler) | ||
else return thisHandler ? thisHandler() : false | ||
}, | ||
start: (portId) => { | ||
innerInteraction.start?.(portId) | ||
parent?.interaction.start?.(portId) | ||
}, | ||
edit: (portId, value) => { | ||
innerInteraction.edit?.(portId, value) | ||
parent?.interaction.edit?.(portId, value) | ||
}, | ||
end: (portId) => { | ||
this.activeInteraction = undefined | ||
innerInteraction.end?.(portId) | ||
parent?.interaction.end?.(portId) | ||
}, | ||
} | ||
} | ||
|
||
static New(input: WidgetInput, myInteraction: WidgetEditInteraction) { | ||
return new WidgetEditHandler(input.portId, myInteraction, input.editHandler) | ||
} | ||
|
||
cancel() { | ||
if (this.activeInteraction) { | ||
this.interactionHandler.cancel(this.activeInteraction) | ||
} | ||
} | ||
|
||
start() { | ||
this.interactionHandler.setCurrent(this.interaction) | ||
for ( | ||
let handler: WidgetEditHandler | undefined = this; | ||
handler != null; | ||
handler = handler.parent | ||
) { | ||
handler.activeInteraction = this.interaction | ||
} | ||
this.interaction.start?.(this.portId) | ||
} | ||
|
||
edit(value: Ast.Owned | string) { | ||
this.interaction.edit?.(this.portId, value) | ||
} | ||
|
||
end() { | ||
if (this.activeInteraction) { | ||
this.interactionHandler.end(this.activeInteraction) | ||
this.activeInteraction.end?.(this.portId) | ||
} | ||
} | ||
|
||
isActive() { | ||
return this.activeInteraction ? this.interactionHandler.isActive(this.activeInteraction) : false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters