title: $:/core/macros/keyboard-driven-input
tags: $:/tags/Macro

\procedure prefix.bracket() [
\procedure suffix.bracket() ]

\function tf.change-input-tab.next-tab() [[all]addprefix<prefix.bracket>addsuffix<prefix.bracket>addsuffix[shadows+tiddlers]addsuffix<suffix.bracket>addsuffix[tag<tag>!has]addsuffix<prefix.bracket>addsuffix[draft.of]addsuffix<suffix.bracket>addsuffix<beforeafter>addsuffix[<currentState>]addsuffix<suffix.bracket>addsuffix[ :else]addsuffix<prefix.bracket>addsuffix<prefix.bracket>addsuffix<beforeafter>addsuffix<suffix.bracket>addsuffix[match]addsuffix<prefix.bracket>addsuffix[after]addsuffix<suffix.bracket>addsuffix[then<firstTab>]addsuffix<suffix.bracket>addsuffix[ :else]addsuffix<prefix.bracket>addsuffix<prefix.bracket>addsuffix<beforeafter>addsuffix<suffix.bracket>addsuffix[match]addsuffix<prefix.bracket>addsuffix[before]addsuffix<suffix.bracket>addsuffix[then<lastTab>]addsuffix<suffix.bracket>]

\procedure change-input-tab(stateTitle,tag,beforeafter,defaultState,actions)
\whitespace trim
<$set name="tabsList" filter="[<explicitTabList>!is[blank]enlist-input[]] :else[all[shadows+tiddlers]tag<tag>!has[draft.of]]">
	<$let
		currentState={{{ [<stateTitle>!is[missing]get[text]] :else[<defaultState>] }}}
		firstTab={{{ [enlist<tabsList>nth[1]] }}}
		lastTab={{{ [enlist<tabsList>last[]] }}}
		nextTabSubFilter=<<tf.change-input-tab.next-tab>>
		nextTab={{{ [subfilter<nextTabSubFilter>] }}}
	>
		<$action-setfield $tiddler=<<stateTitle>> text=<<nextTab>>/>
		<<actions>>
	</$let>
</$set>
\end

\procedure keyboard-input-actions()
\whitespace trim
<%if [<index>match[]] %>
	<$action-setfield $tiddler=<<storeTitle>> text={{{ [<tiddler>get<field>] }}}/>
<% endif %>
<%if [<index>!match[]] %>
	<$action-setfield $tiddler=<<storeTitle>> text={{{ [<tiddler>getindex<index>] }}}/>
<% endif %>
\end

\procedure input-next-actions-inner()
\whitespace trim
<%if [<nextItem>minlength[1]] %>
	<$action-setfield $tiddler=<<selectionStateTitle>> text=<<nextItem>>/>
	<%if [<index>match[]] %>
		<$action-setfield $tiddler=<<tiddler>> $field=<<field>> $value={{{ [<nextItem>] :and[splitregexp[(?:.(?!-))+$]] }}}/>
	<% endif %>
	<%if [<index>!match[]] %>
		<$action-setfield $tiddler=<<tiddler>> $index=<<index>> $value={{{ [<nextItem>] :and[splitregexp[(?:.(?!-))+$]] }}}/>
	<% endif %>
	<$action-setfield $tiddler=<<refreshTitle>> text="yes"/>
<% endif %>
\end

\procedure input-next-actions-after()
\whitespace trim
<%if [<storeTitle>get[text]minlength<filterMinLength>] [<filterMinLength>match[0]] %>
	<$let
		userInput={{{ [<storeTitle>get[text]] }}}
		selectedItem={{{ [<selectionStateTitle>get[text]] }}}
		configTiddler={{{ [subfilter<configTiddlerFilter>] }}}
		primaryListFilter={{{ [<configTiddler>get<firstSearchFilterField>] }}}
		secondaryListFilter={{{ [<configTiddler>get<secondSearchFilterField>] }}}
	>
		<$set
			name="filteredList"
			filter="[subfilter<primaryListFilter>addsuffix[-primaryList]] :all[subfilter<secondaryListFilter>addsuffix[-secondaryList]]"
		>
			<$let
				nextItem={{{ [enlist<filteredList>after<selectedItem>] :else[enlist<filteredList>nth[1]] }}}
				firstItem={{{ [enlist<filteredList>nth[1]] }}}
				lastItem={{{ [enlist<filteredList>last[]] }}}
			>
				<%if [<selectedItem>match<firstItem>!match<lastItem>] [<selectedItem>!match<firstItem>!match<lastItem>] %>
					<<input-next-actions-inner>>
				<%elseif [<selectedItem>match<lastItem>!match<firstItem>] %>
					<$set name="nextItem" value={{{ [<userInput>addsuffix[-userInput]] }}}>
						<<input-next-actions-inner>>
					</$set>
				<%elseif [<selectedItem>match<firstItem>match<lastItem>] %>
					<$set name="nextItem" value={{{ [<userInput>addsuffix[-userInput]] }}}>
						<<input-next-actions-inner>>
					</$set>
				<% endif %>
			</$let>
		</$set>
	</$let>
<% endif %>
\end

\procedure input-next-actions-before()
\whitespace trim
<%if [<storeTitle>get[text]minlength<filterMinLength>] [<filterMinLength>match[0]] %>
	<$let
		userInput={{{ [<storeTitle>get[text]] }}}
		selectedItem={{{ [<selectionStateTitle>get[text]] }}}
		configTiddler={{{ [subfilter<configTiddlerFilter>] }}}
		primaryListFilter={{{ [<configTiddler>get<firstSearchFilterField>] }}}
		secondaryListFilter={{{ [<configTiddler>get<secondSearchFilterField>] }}}
	>
		<$set
			name="filteredList"
			filter="[subfilter<primaryListFilter>addsuffix[-primaryList]] :all[subfilter<secondaryListFilter>addsuffix[-secondaryList]]"
		>
			<$let
				nextItem={{{ [enlist<filteredList>before<selectedItem>] :else[enlist<filteredList>reverse[]nth[1]] }}}
				firstItem={{{ [enlist<filteredList>nth[1]] }}}
				lastItem={{{ [enlist<filteredList>last[]] }}}
			>
				<%if [<selectedItem>match<firstItem>!match<lastItem>] %>
					<$set name="nextItem" value={{{ [<userInput>addsuffix[-userInput]] }}}>
						<<input-next-actions-inner>>
					</$set>
				<%elseif [<selectedItem>match<lastItem>!match<firstItem>] [<selectedItem>!match<firstItem>!match<lastItem>] %>
					<<input-next-actions-inner>>
				<%elseif [<selectedItem>match<firstItem>match<lastItem>] %>
					<$set name="nextItem" value={{{ [<userInput>addsuffix[-userInput]] }}}>
						<<input-next-actions-inner>>
					</$set>
				<% endif %>
			</$let>
		</$set>
	</$let>
<% endif %>
\end

\procedure keyboard-driven-input(tiddler,storeTitle,field:"text",index:"",tag:"input",type,focus:"",inputAcceptActions,inputAcceptVariantActions,inputCancelActions,placeholder:"",default:"",class,focusPopup,rows,minHeight,tabindex,size,autoHeight,filterMinLength:"0",refreshTitle,selectionStateTitle,cancelPopups:"",configTiddlerFilter,firstSearchFilterField:"first-search-filter",secondSearchFilterField:"second-search-filter")
\whitespace trim
\procedure keyboard-driven-input-actions()
<%if [<event-key-descriptor>match[((input-accept))]] %>
<$transclude $variable=inputAcceptActions $fillignore=yes />
<%elseif [<event-key-descriptor>match[((input-accept-variant))]] %>
<$transclude $variable=inputAcceptVariantActions $fillignore=yes />
<%elseif [<event-key-descriptor>match[((input-up))]] %>
<$transclude $variable=input-next-actions-before $fillignore=yes />
<%elseif [<event-key-descriptor>match[((input-down))]] %>
<$transclude $variable=input-next-actions-after $fillignore=yes />
<%elseif [<event-key-descriptor>match[((input-cancel))]] %>
<$transclude $variable=inputCancelActions $fillignore=yes />
<%endif%>
\end keyboard-driven-input-actions

<$keyboard key="((input-accept)) ((input-accept-variant)) ((input-up)) ((input-down)) ((input-cancel))" actions=<<keyboard-driven-input-actions>>>
	<$edit-text
		tiddler=<<tiddler>> field=<<field>> index=<<index>>
		inputActions=<<keyboard-input-actions>> tag=<<tag>> class=<<class>>
		placeholder=<<placeholder>> default=<<default>> focusPopup=<<focusPopup>>
		focus=<<focus>> type=<<type>> rows=<<rows>> minHeight=<<minHeight>>
		tabindex=<<tabindex>> size=<<size>> autoHeight=<<autoHeight>>
		refreshTitle=<<refreshTitle>> cancelPopups=<<cancelPopups>>
	/>
</$keyboard>
\end keyboard-driven-input
