title: $:/core/macros/tag-picker
tags: $:/tags/Macro
first-search-filter: [subfilter<tagListFilter>!is[system]search:title<userInput>]
second-search-filter: [subfilter<tagListFilter>is[system]search:title<userInput>]

<!--
	Fields: "first-search-filter", "second-search-filter" are assigned to the keyboard-driven input macro with parameter: "configTiddlerFilter"
	They __need to be the same__ as used for variables: "nonSystemTagsFilter" and "systemTagsFilter". See code below!
-->

\whitespace trim

<!-- tf.tagpicker-dropdown-id is needed if several tap-pickers are shown in one tiddler -->
\function tf.tagpicker-dropdown-id()
	[<qualify $:/state/popup/tags-auto-complete>]
	[[$(saveTiddler)$-[$(tagField)$-$(tagListFilter)$]substitute[]sha256[]] :and[join[/]]
\end

\function tf.tagpicker-dropdown-class() [<tf.tagpicker-dropdown-id>sha256[]addprefix[tc-]]
\function tf.get-tagpicker-focus-selector() [<tf.tagpicker-dropdown-class>addprefix[.]] .tc-popup-handle :and[join[ ]]

<!-- clean up temporary tiddlers, so the next "pick" starts with a clean input -->
<!-- This could probably be optimized / removed if we would use different temp-tiddlers
	(future improvement because keeping track is complex for humans)
-->
\procedure delete-tag-state-tiddlers()
<$action-deletetiddler $filter="[<newTagNameTiddler>] [<storeTitle>] [<tagSelectionState>]"/>
\end

<!-- trigger __toggle tag__ by keyboard -->
\procedure add-tag-actions()
<$let tag=<<_tf.getTag>> >
	<$action-listops $tiddler=<<saveTiddler>> $field=<<tagField>> $subfilter=':and[toggle<tag>trim[]]'/>
	<%if [<tag>] :intersection[<saveTiddler>get<tagField>enlist-input[]] %>
		<!-- tag has been removed - do nothing -->
	<%else%>
		<<actions>>
	<%endif%>
	<<delete-tag-state-tiddlers>>
	<$action-setfield $tiddler=<<refreshTitle>> text="yes"/>
</$let>
\end
<!-- <$action-log  /> -->

<!-- ESC key removes the text from the input
The second ESC tries to close the "draft tiddler"
-->
\procedure clear-tags-actions-inner()
<%if [<storeTitle>has[text]] :else[<newTagNameTiddler>has[text]] %>
	<<delete-tag-state-tiddlers>>
<%else%>
		<<cancel-delete-tiddler-actions "cancel">>
<%endif%>
\end

<!-- triggered by keyboard only -->
\procedure clear-tags-actions()
<$let userInput=<<_tf.getUserInput>> >
	<!-- this list __cannot__ be transformed to conditional IF. The list variable is used! -->
	<$list filter="[<newTagNameTiddler>get[text]!match<userInput>]" >
		<$list-empty>
			<<clear-tags-actions-inner>>
		</$list-empty>
		<$action-setfield $tiddler=<<newTagNameTiddler>> text=<<userInput>>/>
		<$action-setfield $tiddler=<<refreshTitle>> text="yes"/>
	</$list>
</$let>
\end

<!-- similar to add-tag-actions __but__ add-only -->
\procedure add-button-actions()
<$action-listops $tiddler=<<saveTiddler>> $field=<<tagField>> $subfilter="[<tag>trim[]]"/>
<<actions>>
<<delete-tag-state-tiddlers>>
<$action-sendmessage $message="tm-focus-selector" $param=<<tf.get-tagpicker-focus-selector>>/>
\end
<!-- <$action-log /> -->

<!-- create dropdown list -->
\procedure tag-picker-listTags(filter, suffix, empty)
<$let userInput=<<_tf.getUserInput>> >
	<$list filter="[<userInput>minlength{$:/config/Tags/MinLength}limit[1]]"
		emptyMessage="<div class='tc-search-results'>{{$:/language/Search/Search/TooShort}}</div>" variable="listItem"
	>
		<$list filter=<<filter>> variable="tag">
			<$list-empty>
				<span class="tc-small-gap-left"><<empty>></span>
			</$list-empty>
			<!-- The buttonClasses filter is used to define tc-tag-button-selected state -->
			<!-- tf.get-tagpicker-focus-selector has to be resolved for $:/core/ui/TagPickerTagTemplate,
				othwerwise qualify in tf.tagpicker-dropdown-id causes problems -->
			<$let currentTiddler=<<tag>>
				button-classes=`tc-btn-invisible ${[<tag>addsuffix<suffix>] :except[<tagSelectionState>get[text]] :then[[]] ~tc-tag-button-selected }$`
				get-tagpicker-focus-selector=`${[<tf.get-tagpicker-focus-selector>]}$`
			>
				{{||$:/core/ui/TagPickerTagTemplate}}
			</$let>
		</$list>
	</$list>
</$let>
\end

<!-- tag-picker-inner is the main function -->
\procedure tag-picker-inner()
<div class={{{ [[tc-edit-add-tag]] [<tf.tagpicker-dropdown-class>] :and[join[ ]] }}}>
	<div class="tc-edit-add-tag-ui">
		<span class="tc-add-tag-name tc-small-gap-right">
			<$transclude $variable="keyboard-driven-input"
				tiddler=<<newTagNameTiddler>>
				storeTitle=<<storeTitle>>
				refreshTitle=<<refreshTitle>>
				selectionStateTitle=<<tagSelectionState>>
				inputAcceptActions=<<add-tag-actions>>
				inputAcceptVariantActions=<<save-tiddler-actions>>
				inputCancelActions=<<clear-tags-actions>>
				tag="input"
				placeholder={{$:/language/EditTemplate/Tags/Add/Placeholder}}
				focusPopup=<<tf.tagpicker-dropdown-id>>
				class="tc-edit-texteditor tc-popup-handle"
				tabindex=<<tabIndex>>
				focus={{{ [{!!draft.of}is[tiddler]then{$:/config/AutoFocusEdit}match[tags]then[true]] :else[{$:/config/AutoFocus}match[tags]then[true]] :else[[false]] }}}
				filterMinLength={{$:/config/Tags/MinLength}}
				cancelPopups=<<cancelPopups>>
				configTiddlerFilter="[[$:/core/macros/tag-picker]]"
			/>
		</span>
		<$button popup=<<tf.tagpicker-dropdown-id>> class="tc-btn-invisible tc-btn-dropdown"
			tooltip={{$:/language/EditTemplate/Tags/Dropdown/Hint}} aria-label={{$:/language/EditTemplate/Tags/Dropdown/Caption}}
		>
			{{$:/core/images/down-arrow}}
		</$button>
		<%if [<storeTitle>has[text]] %>
			<$button actions=<<delete-tag-state-tiddlers>> class="tc-btn-invisible tc-small-gap tc-btn-dropdown"
				tooltip={{$:/language/EditTemplate/Tags/ClearInput/Hint}} aria-label={{$:/language/EditTemplate/Tags/ClearInput/Caption}}
			>
				{{$:/core/images/close-button}}
			</$button>
		<%endif%>
		<span class="tc-add-tag-button tc-small-gap-left">
			<$let tag=<<_tf.getTag>>>
				<$button set=<<newTagNameTiddler>> actions=<<add-button-actions>> >
					{{$:/language/EditTemplate/Tags/Add/Button}}
				</$button>
			</$let>
		</span>
	</div>
	<div class="tc-block-dropdown-wrapper">
		<%if [<tf.tagpicker-dropdown-id>has[text]] %>
			<div class="tc-block-dropdown tc-block-tags-dropdown">
				<$transclude $variable="tag-picker-listTags" filter=<<nonSystemTagsFilter>> suffix="-primaryList" empty={{$:/language/EditTemplate/Tags/EmptyMessage}}/>
				<hr>
				<$transclude $variable="tag-picker-listTags" filter=<<systemTagsFilter>> suffix="-secondaryList" empty={{$:/language/EditTemplate/Tags/EmptyMessage/System}}/>
			</div>
		<%endif%>
	</div>
</div>
\end

<!-- prepare all variables for tag-picker keyboard handling -->
\procedure tag-picker(actions, tagField:"tags", tiddler, tagListFilter:"[tags[]sort[]]")

\function _tf.getUserInput() [<storeTitle>get[text]]
\function _tf.getTag() [<newTagNameTiddler>get[text]]
<!-- Use this function if tag-picker is a stand alone macro. Otherwise use "newTagNameTiddler" defined for fieldmangler in EditTemplate -->
\function _tf.makeTagNameTiddler() [[$:/temp/NewTagName]] [<tagField>!match[tags]] :and[join[/]] [<qualify>] :and[join[]]

<!-- keep those variables because they may "bleed" into macros using old syntax -->
<!-- "nonSystemTagsFilter", "systemTagsFilter" __need to be the same__ as fields: "first-search-filter", "second-search-filter" -->
<$let
	palette={{$:/palette}}
	colourA={{{ [<palette>getindex[foreground]] }}}
	colourB={{{ [<palette>getindex[background]] }}}
	fallbackTarget={{{ [<palette>getindex[tag-background]] }}}

	saveTiddler={{{ [<tiddler>is[blank]then<currentTiddler>else<tiddler>] }}}

	newTagNameTiddler={{{ [[newTagNameTiddler]is[variable]then<newTagNameTiddler>] :else[<_tf.makeTagNameTiddler>] }}}
	storeTitle={{{ [[$:/temp/NewTagName/input]] [<tagField>!match[tags]] :and[join[/]] [<qualify>] :and[join[]] }}}

	newTagNameSelectionTiddlerQualified=<<qualify "$:/temp/NewTagName/selected-item">>
	tagSelectionState={{{ [<newTagNameSelectionTiddler>!match[]] :else[<newTagNameSelectionTiddlerQualified>] }}}

	refreshTitle=<<qualify "$:/temp/NewTagName/refresh">>

	nonSystemTagsFilter="[subfilter<tagListFilter>!is[system]search:title<userInput>]"
	systemTagsFilter="[subfilter<tagListFilter>is[system]search:title<userInput>]"

	cancelPopups="yes"
>
	<$transclude $variable="tag-picker-inner"/>
</$let>
\end
