Last active
August 3, 2020 21:44
-
-
Save DaveWelling/74b101f70c55c642a6c57ff0be1218b0 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
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
/* eslint camelcase: 0 */ // Ignore camelcase in this file. | |
// To get a visualization of this finite state machine, | |
// to to https://xstate.js.org/viz/?gist=74b101f70c55c642a6c57ff0be1218b0. | |
// You may have to copy the code below and replace the | |
// 'CODE' section in the link above. | |
// (HINT: It is easier to copy if you collapse all the top level stuff) | |
// ******************************************************** | |
// TO COPY TO VISUALIZATION TOOL LINKED ABOVE, START COPY HERE | |
// ******************************************************** | |
const isTrue = 'isTrue'; | |
const isFalse = 'isFalse'; | |
// A default context for visualization tool - overridden by app. | |
// eslint-disable-next-line no-unused-vars | |
const context = { | |
listOptions: [], | |
currentInputText: '', | |
initialSelectedOptions: [], | |
currentSelectedOptions: [], | |
initialFilters: {}, | |
currentFilters: {}, | |
settings: { | |
autoSelect: false, | |
autoHide: false, | |
multiSelect: false, | |
hNode: {}, | |
formContext: {} | |
}, | |
subscriptions: {}, | |
callbackForTextInputChange: () => {}, | |
onSelectionChanged: () => {} | |
// for reference (it is set later): | |
// focusedListOption: undefined | |
}; | |
const fsmGuards = { | |
c_and: (context, event, { cond }) => | |
cond.conditions.every(childCond => | |
fsmGuards[typeof childCond === 'string' ? childCond : childCond.type](context, event, { | |
cond: childCond | |
}) | |
), | |
c_multiSelect: (context, event, { cond }) => | |
(cond.isFalse && !context.settings.multiSelect) || (!cond.isFalse && context.settings.multiSelect), | |
c_keyEqual: (context, event, { cond }) => { | |
return event.keyValue === cond.keyValue; | |
}, | |
c_listOptionFocused: (context, event, { cond }) => | |
(context.focusedListOption && !cond.isFalse) || (!context.focusedListOption && cond.isFalse), | |
c_onlyOneSelectionOption: (context, event, { cond }) => { | |
return context.listOptions && context.listOptions.length === 1 && !cond.isFalse; | |
}, | |
c_autoHide: (context, event, { cond }) => context.settings.autoHide && !cond.isFalse, | |
c_autoSelect: (context, event, { cond }) => { | |
return context.settings.autoSelect && !cond.isFalse; | |
}, | |
c_atLeastOneListOption: (context, event, { cond }) => context.listOptions.length > 0 && !cond.isFalse, | |
c_currentInputTextEqualOriginal: (context, event, { cond }) => { | |
if (!context.settings) return true; | |
let result; | |
if (context.settings.multiSelect) { | |
result = context.currentInputText === ''; | |
} else { | |
const { propertyName } = context.settings.hNode; | |
result = | |
(!context.initialSelectedOptions && | |
(typeof context.currentInputText === 'undefined' || context.currentInputText === '')) || | |
context.currentInputText === | |
(context.initialSelectedOptions ? context.initialSelectedOptions[propertyName] : ''); | |
} | |
return !cond.isFalse ? result : !result; | |
}, | |
c_incomingInputTextEqualOriginal: (context, event, { cond }) => { | |
if (!context.settings) return true; | |
if (context.settings.multiSelect) { | |
return event.searchTerm === '' && !cond.isFalse; | |
} else { | |
const { propertyName } = context.settings.hNode; | |
const result = | |
(!context.initialSelectedOptions && | |
(typeof event.searchTerm === 'undefined' || event.searchTerm === '')) || | |
event.searchTerm === | |
(context.initialSelectedOptions ? context.initialSelectedOptions[propertyName] : ''); | |
// Invert result if isFalse is passed. | |
return !cond.isFalse ? result : !result; | |
} | |
}, | |
c_enterIsQueued: context => context.enterQueued, | |
c_isLoading: context => context.loadActorRef.state.matches('is_loading'), | |
c_cursorInInput: (context, event, { cond }) => { | |
const result = event.tagName.toLowerCase() === 'input'; | |
return !cond.isFalse ? result : !result; | |
} | |
}; | |
const fsmConfig = { | |
id: 'dropDownFsm', | |
initial: 'is_mounted', | |
context: { | |
// Default this or you will get this error: "A component is changing an uncontrolled input of type text to be controlled" | |
currentInputText: '' | |
}, | |
states: { | |
is_mounted: { | |
on: { | |
FORM_BECOMES_VISIBLE: 'is_preparingToDisplay' | |
} | |
}, | |
is_preparingToDisplay: { | |
entry: ['do_addSubscriptions', 'do_spawnLoadMachine'], | |
on: { | |
LOAD_SUCCESS: { | |
target: 'is_loaded', | |
actions: 'do_updateListOptions' | |
}, | |
VALUE_SELECTED: { | |
actions: ['do_displayValue'] | |
} | |
} | |
}, | |
is_loaded: { | |
on: { | |
FORM_BECOMES_HIDDEN: { | |
target: 'is_mounted', | |
actions: ['do_shutdownLoadMachine'] | |
} | |
}, | |
always: [ | |
{ | |
target: 'is_hidden', | |
cond: { | |
type: 'c_and', | |
conditions: [ | |
{ | |
type: 'c_onlyOneSelectionOption' | |
}, | |
{ | |
type: 'c_autoHide' | |
}, | |
{ | |
type: 'c_autoSelect' | |
} | |
] | |
}, | |
actions: 'do_selectFirstListOption' | |
}, | |
{ | |
target: 'is_hidden', | |
cond: { | |
type: 'c_and', | |
conditions: [ | |
{ | |
type: 'c_onlyOneSelectionOption' | |
}, | |
{ | |
type: 'c_autoHide' | |
} | |
] | |
} | |
}, | |
{ | |
target: 'is_visible', | |
cond: { | |
type: 'c_and', | |
conditions: [ | |
{ | |
type: 'c_onlyOneSelectionOption' | |
}, | |
{ | |
type: 'c_autoSelect' | |
} | |
] | |
}, | |
actions: 'do_selectFirstListOption' | |
}, | |
{ | |
target: 'is_visible', | |
actions: 'do_raiseSelectEvent' | |
} | |
] | |
}, | |
is_visible: { | |
invoke: { | |
id: 'testSelectionInForeignRelation', | |
src: 'svc_testSelectionInForeignRelation' | |
}, | |
initial: 'is_waitingForFocus', | |
on: { | |
FORM_BECOMES_HIDDEN: { | |
target: 'is_mounted', | |
actions: ['do_shutdownLoadMachine', 'do_unsubscribe'] | |
}, | |
SIBLING_DEPENDENCY_CHANGED: { | |
actions: [ | |
'do_composeForeignRelationSelectionFilter', | |
'do_sendFilterToLoad', | |
'do_verifySelectionInForeignRelationDependency' | |
] | |
}, | |
SELECTED_OPTION_NOT_IN_DEPENDENCY: { | |
actions: 'do_clearCurrentSelection' | |
} | |
}, | |
states: { | |
is_waitingForFocus: { | |
entry: 'do_displayValue', | |
on: { | |
GETS_FOCUS: 'is_focused', | |
VALUE_SELECTED: { | |
actions: ['do_raiseSelectEvent', 'do_displayValue'] | |
} | |
} | |
}, | |
is_focused: { | |
entry: ['do_storeOriginalValue'], | |
initial: 'is_ready', | |
on: { | |
LOST_FOCUS: { | |
target: 'is_waitingForFocus', | |
actions: ['do_revertInputText'] | |
}, | |
KEY_DOWN: [ | |
{ | |
// ARROW DOWN from input box | |
cond: { | |
type: 'c_and', | |
conditions: [ | |
{ | |
type: 'c_keyEqual', | |
keyValue: 'ArrowDown' | |
}, | |
{ | |
type: 'c_cursorInInput' | |
} | |
] | |
}, | |
actions: 'do_focusFirstListOption' | |
}, | |
{ | |
// ARROW DOWN from inside list | |
cond: { | |
type: 'c_and', | |
conditions: [ | |
{ | |
type: 'c_keyEqual', | |
keyValue: 'ArrowDown' | |
}, | |
{ | |
type: 'c_cursorInInput', | |
isFalse | |
} | |
] | |
}, | |
actions: 'do_focusNextListOption' | |
}, | |
{ | |
// ARROW UP from inside list | |
cond: { | |
type: 'c_and', | |
conditions: [ | |
{ | |
type: 'c_keyEqual', | |
keyValue: 'ArrowUp' | |
}, | |
{ | |
type: 'c_cursorInInput', | |
isFalse | |
} | |
] | |
}, | |
actions: 'do_focusPreviousListOption' | |
}, | |
{ | |
// ESCAPE | |
target: 'is_focused.is_ready', | |
cond: { | |
type: 'c_keyEqual', | |
keyValue: 'Escape' | |
}, | |
actions: ['do_revertInputText'] | |
}, | |
{ | |
// ENTER ON FOCUSED LIST OPTION | |
target: 'is_waitingForFocus', | |
cond: { | |
type: 'c_and', | |
conditions: [ | |
{ | |
type: 'c_keyEqual', | |
keyValue: 'Enter' | |
}, | |
{ | |
type: 'c_listOptionFocused' | |
} | |
] | |
}, | |
actions: ['do_selectFocusedListOption', 'do_gotoNextControl'] | |
}, | |
{ | |
// ENTER FROM INPUT BOX | |
target: 'is_waitingForFocus', | |
cond: { | |
type: 'c_and', | |
conditions: [ | |
{ | |
type: 'c_keyEqual', | |
keyValue: 'Enter' | |
}, | |
{ | |
type: 'c_isLoading', | |
isFalse | |
}, | |
{ | |
type: 'c_atLeastOneListOption' | |
} | |
] | |
}, | |
actions: 'do_selectFirstListOption' | |
}, | |
{ | |
cond: { | |
type: 'c_and', | |
conditions: [ | |
{ | |
type: 'c_keyEqual', | |
keyValue: 'Enter' | |
}, | |
{ | |
type: 'c_isLoading' | |
} | |
] | |
}, | |
actions: 'do_queueEnterForLoadSuccess' | |
} | |
], | |
VALUE_SELECTED: [ | |
{ | |
target: 'is_waitingForFocus', | |
cond: { | |
type: 'c_multiSelect', | |
isFalse | |
}, | |
actions: ['do_raiseSelectEvent', 'do_displayValue'] | |
}, | |
{ | |
target: 'is_focused.is_ready', | |
cond: { | |
type: 'c_multiSelect', | |
isTrue | |
}, | |
actions: ['do_raiseSelectEvent', 'do_displayValue'] | |
} | |
], | |
LOAD_SUCCESS: [ | |
{ | |
target: 'is_waitingForFocus', | |
cond: { | |
type: 'c_and', | |
conditions: ['c_enterIsQueued', 'c_atLeastOneListOption'] | |
}, | |
actions: ['do_clearEnterQueue', 'do_selectFirstListOption', 'do_gotoNextControl'] | |
}, | |
{ | |
target: 'is_focused.is_ready', | |
cond: 'c_currentInputTextEqualOriginal', | |
actions: 'do_updateListOptions' | |
}, | |
{ | |
target: 'is_focused.is_inputDirty', | |
cond: { | |
type: 'c_currentInputTextEqualOriginal', | |
isFalse | |
}, | |
actions: 'do_updateListOptions' | |
} | |
], | |
LOAD_FAILURE: { | |
actions: 'do_clearEnterQueue' | |
}, | |
SEARCH_TEXT_CHANGED: [ | |
{ | |
target: 'is_focused.is_ready', | |
cond: 'c_incomingInputTextEqualOriginal', | |
actions: ['do_setCurrentInputText', 'do_composeSearchFilter', 'do_sendFilterToLoad'] | |
}, | |
{ | |
target: 'is_focused.is_inputDirty', | |
cond: { | |
type: 'c_incomingInputTextEqualOriginal', | |
isFalse | |
}, | |
actions: ['do_setCurrentInputText', 'do_composeSearchFilter', 'do_sendFilterToLoad'] | |
} | |
], | |
LIST_OPTION_FOCUSED: { | |
actions: 'do_setFocusedListOption' | |
} | |
}, | |
states: { | |
is_ready: {}, | |
is_inputDirty: {} | |
} | |
} | |
} | |
}, | |
is_hidden: { | |
on: { | |
SIBLING_DEPENDENCY_CHANGED: { | |
target: 'is_preparingToDisplay', | |
actions: [ | |
'do_composeForeignRelationSelectionFilter', | |
'do_sendFilterToLoad', | |
'do_verifySelectionInForeignRelationDependency' | |
] | |
} | |
} | |
} | |
} | |
}; | |
// TO COPY TO VISUALIZATION UNCOMMENT THIS | |
const ddMachine = Machine({ ...fsmConfig, context }, { guards: fsmGuards }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment