|
|
|
|
@@ -38,31 +38,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defn typeahead- [params]
|
|
|
|
|
[:div.relative {:x-data (hx/json {:open false
|
|
|
|
|
:baseUrl (if (str/includes? (:url params) "?")
|
|
|
|
|
[:div.relative {:x-data (hx/json { :baseUrl (if (str/includes? (:url params) "?")
|
|
|
|
|
(str (:url params) "&q=")
|
|
|
|
|
(str (:url params) "?q="))
|
|
|
|
|
:value {:value ((:value-fn params identity) (:value params)) :label ((:content-fn params identity) (:value params))}
|
|
|
|
|
:tippy nil
|
|
|
|
|
:search ""
|
|
|
|
|
:active -1
|
|
|
|
|
:elements (if ((:value-fn params identity) (:value params))
|
|
|
|
|
[{:value ((:value-fn params identity) (:value params)) :label ((:content-fn params identity) (:value params))}]
|
|
|
|
|
[])
|
|
|
|
|
:popper nil
|
|
|
|
|
:warning_badge nil})
|
|
|
|
|
})
|
|
|
|
|
:x-modelable "value.value"
|
|
|
|
|
:x-model (:x-model params)
|
|
|
|
|
:x-init "popper = Popper.createPopper($refs.input, $refs.dropdown, {placement: 'bottom-start', strategy: 'fixed', modifiers: {name: 'offset', options: {offset: [0, 10]}}})
|
|
|
|
|
warning_badge = Popper.createPopper($refs.warning_badge, $refs.warning_pop, {placement: 'top', strategy: 'fixed', modifiers: {name: 'offset', options: {offset: [10,0 ]}}})"}
|
|
|
|
|
:x-model (:x-model params)}
|
|
|
|
|
(if (:disabled params)
|
|
|
|
|
[:span {:x-text "value.label"}]
|
|
|
|
|
[:a {:class (-> (hh/add-class (or (:class params) "") default-input-classes)
|
|
|
|
|
(hh/add-class "cursor-pointer"))
|
|
|
|
|
"@click.prevent" "open = !open; popper.update()"
|
|
|
|
|
"@keydown.down.prevent.stop" "open = true; popper.update()"
|
|
|
|
|
"@keydown.backspace" "value = {value: '', label: '' }"
|
|
|
|
|
"x-tooltip.on.click" "{content: ()=>$refs.dropdown.innerHTML, placement: 'bottom', onMount(i) {htmx.process(i.popper); }, popperOptions: {strategy: 'fixed', modifiers: [{name: 'flip', options: {fallbackPlacements: ['top']}}]}, theme: 'dropdown', allowHTML: true, interactive:true}"
|
|
|
|
|
"@keydown.down.prevent.stop" "tippy.show();"
|
|
|
|
|
"@keydown.backspace" "tippy.hide(); value = {value: '', label: '' }"
|
|
|
|
|
:tabindex 0
|
|
|
|
|
:x-init (:x-init params)
|
|
|
|
|
:x-init (str "$nextTick(() => tippy = $el.__x_tippy); " (:x-init params))
|
|
|
|
|
:x-ref "input"}
|
|
|
|
|
[:input (-> params
|
|
|
|
|
(dissoc :class)
|
|
|
|
|
@@ -81,63 +78,50 @@
|
|
|
|
|
[:span.flex-grow.text-left {"x-text" "value.label"}]
|
|
|
|
|
[:div {:class "w-3 h-3 m-1 inline ml-1 justify-self-end text-gray-500 self-center"}
|
|
|
|
|
svg/drop-down]
|
|
|
|
|
[:div {:x-show "value.warning"
|
|
|
|
|
:x-ref "warning_badge"
|
|
|
|
|
:x-effect "if (value.warning) { $nextTick(()=> warning_badge.update()) }"}
|
|
|
|
|
(tags/badge- {:class "peer"} "!")
|
|
|
|
|
[:div {:x-show "value.warning" }
|
|
|
|
|
(tags/badge- {:class "peer"
|
|
|
|
|
:x-tooltip "value.warning"} "!") ]]])
|
|
|
|
|
|
|
|
|
|
[:template {:x-ref "dropdown"}
|
|
|
|
|
[:ul.dropdown-contents {:class "bg-gray-100 dark:bg-gray-600 ring-1"
|
|
|
|
|
"@keydown.escape" "tippy.hide(); value = {value: '', label: '' }; "
|
|
|
|
|
:x-destroy "if ($refs.input) {$refs.input.focus();}"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[:div {:x-show "value.warning"
|
|
|
|
|
:x-ref "warning_pop"
|
|
|
|
|
:class "hidden peer-hover:block bg-red-50 dark:bg-gray-600 rounded-lg shadow-2xl w-max z-50 p-4"
|
|
|
|
|
:x-text "value.warning"}]]]])
|
|
|
|
|
|
|
|
|
|
[:ul.dropdown-contents {:class "bg-gray-100 dark:bg-gray-600 rounded-lg shadow-2xl w-max z-50 ring-1"
|
|
|
|
|
"x-ref" "dropdown"
|
|
|
|
|
"@keydown.escape" "open = false; value = {value: '', label: '' }"
|
|
|
|
|
"x-transition:enter" "ease-[cubic-bezier(.3,2.3,.6,1)] duration-200"
|
|
|
|
|
"x-transition:enter-start" "!opacity-0"
|
|
|
|
|
"x-transition:enter-end" "!opacity-1"
|
|
|
|
|
"x-transition:leave" "ease-out duration-200"
|
|
|
|
|
"x-transition:leave-start" "!opacity-1"
|
|
|
|
|
"x-transition:leave-end" "!opacity-0"
|
|
|
|
|
"x-show " "open"
|
|
|
|
|
"x-trap" "open"
|
|
|
|
|
"@click.outside" "open=false;"}
|
|
|
|
|
|
|
|
|
|
[:input {:type "text"
|
|
|
|
|
:class (-> (:class params)
|
|
|
|
|
(or "")
|
|
|
|
|
(hh/add-class default-input-classes)
|
|
|
|
|
(hh/replace-wildcard ["rounded" "border"] "border-bottom bg-gray-100 rounded-t-lg w-full"))
|
|
|
|
|
"x-model" "search"
|
|
|
|
|
"placeholder" (:placeholder params)
|
|
|
|
|
"@keydown.down.prevent" "active ++; active = active >= elements.length - 1 ? elements.length - 1 : active"
|
|
|
|
|
"@keydown.up.prevent" "active --; active = active < 0 ? 0 : active"
|
|
|
|
|
"@keydown.enter.prevent.stop" "open = false; value = elements.length > 0 ? $data.elements[active] : {'value': '', label: ''};"
|
|
|
|
|
"x-init" "$watch('search', s => { if($el.value.length > 2) {fetch(baseUrl + s).then(data=>data.json()).then(data => {elements = data; active=-1}) }})"}]
|
|
|
|
|
[:div.dropdown-options {:class "rounded-b-lg overflow-hidden"}
|
|
|
|
|
[:template {:x-for "(element, index) in elements"}
|
|
|
|
|
[:li [:a {:class "px-4 py-2 flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-300 [&.active]:dark:bg-primary-700 text-gray-800 dark:text-gray-100"
|
|
|
|
|
:href "#"
|
|
|
|
|
":class" "active == index ? 'active' : ''"
|
|
|
|
|
"@mouseover" "active = index"
|
|
|
|
|
"@mouseout" "active = -1"
|
|
|
|
|
"@click.prevent" "value = element; open=false; "
|
|
|
|
|
"x-html" "element.label"}]]]
|
|
|
|
|
[:template {:x-if "elements.length == 0"}
|
|
|
|
|
[:li {:class "px-4 py-2 flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-500 text-gray-800 dark:text-gray-100 text-xs "}
|
|
|
|
|
"No results found"]]]]])
|
|
|
|
|
[:input {:type "text"
|
|
|
|
|
:autofocus true
|
|
|
|
|
:class (-> (:class params)
|
|
|
|
|
(or "")
|
|
|
|
|
(hh/add-class default-input-classes)
|
|
|
|
|
(hh/replace-wildcard ["rounded" "border"] "border-bottom bg-gray-100 rounded-t-lg w-full"))
|
|
|
|
|
"x-model" "search"
|
|
|
|
|
"placeholder" (:placeholder params)
|
|
|
|
|
"@keydown.down.prevent" "active ++; active = active >= elements.length - 1 ? elements.length - 1 : active"
|
|
|
|
|
"@keydown.up.prevent" "active --; active = active < 0 ? 0 : active"
|
|
|
|
|
"@keydown.enter.prevent.stop" "tippy.hide(); value = elements.length > 0 ? $data.elements[active] : {'value': '', label: ''}; $refs.input.focus()"
|
|
|
|
|
"x-init" "$el.focus(); $watch('search', s => { if($el.value.length > 2) {fetch(baseUrl + s).then(data=>data.json()).then(data => {elements = data; active=-1; tippy.popperInstance.update()}) }})"}]
|
|
|
|
|
[:div.dropdown-options {:class "rounded-b-lg overflow-hidden"}
|
|
|
|
|
[:template {:x-for "(element, index) in elements"}
|
|
|
|
|
[:li [:a {:class "px-4 py-2 flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-300 [&.active]:dark:bg-primary-700 text-gray-800 dark:text-gray-100"
|
|
|
|
|
:href "#"
|
|
|
|
|
":class" "active == index ? 'active' : ''"
|
|
|
|
|
"@mouseover" "active = index"
|
|
|
|
|
"@mouseout" "active = -1"
|
|
|
|
|
"@click.prevent" "value = element; tippy.hide(); $refs.input.focus()"
|
|
|
|
|
"x-html" "element.label"}]]]
|
|
|
|
|
[:template {:x-if "elements.length == 0"}
|
|
|
|
|
[:li {:class "px-4 py-2 flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-500 text-gray-800 dark:text-gray-100 text-xs "}
|
|
|
|
|
"No results found"]]]]]])
|
|
|
|
|
|
|
|
|
|
(defn multi-typeahead- [params]
|
|
|
|
|
[:div.relative {:x-data (hx/json {:open false
|
|
|
|
|
:baseUrl (if (str/includes? (:url params) "?")
|
|
|
|
|
[:div.relative {:x-data (hx/json {:baseUrl (if (str/includes? (:url params) "?")
|
|
|
|
|
(str (:url params) "&q=")
|
|
|
|
|
(str (:url params) "?q="))
|
|
|
|
|
:value (map (fn [v] ((:value-fn params identity) v))
|
|
|
|
|
(:value params))
|
|
|
|
|
:tippy nil
|
|
|
|
|
:lookup (into {}
|
|
|
|
|
(map (fn [v] [ ((:value-fn params identity) v)
|
|
|
|
|
(map (fn [v] [((:value-fn params identity) v)
|
|
|
|
|
((:content-fn params identity) v)])
|
|
|
|
|
(:value params)))
|
|
|
|
|
:x-init (str "$watch('value', v => $dispatch('change')); ")
|
|
|
|
|
@@ -146,42 +130,40 @@
|
|
|
|
|
:elements (if ((:value-fn params identity) (:value params))
|
|
|
|
|
[{:value ((:value-fn params identity) (:value params)) :label ((:content-fn params identity) (:value params))}]
|
|
|
|
|
[])
|
|
|
|
|
:popper nil
|
|
|
|
|
:warning_badge nil})
|
|
|
|
|
:x-ref "r"})
|
|
|
|
|
;; :x-modelable "value.value" TODO
|
|
|
|
|
;; :x-model (:x-model params) TODO
|
|
|
|
|
:x-init "value=new Set(value || []); popper = Popper.createPopper($refs.input, $refs.dropdown, {placement: 'bottom-start', strategy: 'fixed', modifiers: {name: 'offset', options: {offset: [0, 10]}}})
|
|
|
|
|
warning_badge = Popper.createPopper($refs.warning_badge, $refs.warning_pop, {placement: 'top', strategy: 'fixed', modifiers: {name: 'offset', options: {offset: [10,0 ]}}})"}
|
|
|
|
|
:x-init "value=new Set(value || []); "}
|
|
|
|
|
(if (:disabled params)
|
|
|
|
|
[:span {:x-text "value.label"}]
|
|
|
|
|
[:a {:class (-> (hh/add-class (or (:class params) "") default-input-classes)
|
|
|
|
|
(hh/add-class "cursor-pointer"))
|
|
|
|
|
"@click.prevent" "open = !open; popper.update()"
|
|
|
|
|
"@keydown.down.prevent.stop" "open = true; popper.update()"
|
|
|
|
|
"x-tooltip.on.click.prevent" "{content: ()=>$refs.dropdown.innerHTML, placement: 'bottom', onMount(i) {htmx.process(i.popper); }, popperOptions: {strategy: 'fixed', modifiers: [{name: 'flip', options: {fallbackPlacements: ['top']}}]}, theme: 'dropdown', allowHTML: true, interactive:true}"
|
|
|
|
|
"@keydown.down.prevent.stop" "tippy.show();"
|
|
|
|
|
"@keydown.backspace" "tippy.hide(); value=new Set( []);"
|
|
|
|
|
:tabindex 0
|
|
|
|
|
:x-init (:x-init params)
|
|
|
|
|
:x-init (str "$nextTick(() => tippy = $el.__x_tippy); " (:x-init params))
|
|
|
|
|
:x-ref "input"}
|
|
|
|
|
[:template {:x-for "v in Array.from(value.values())"}
|
|
|
|
|
[:input (-> params
|
|
|
|
|
(dissoc :class :value-fn :content-fn :placeholder :x-model)
|
|
|
|
|
(assoc
|
|
|
|
|
:type "hidden"
|
|
|
|
|
"x-bind:value" "v"
|
|
|
|
|
))]]
|
|
|
|
|
"x-bind:value" "v"))]]
|
|
|
|
|
[:div.flex.w-full.justify-items-stretch
|
|
|
|
|
[:div.flex-grow.flex
|
|
|
|
|
[:template {:x-if "value.size > 0"}
|
|
|
|
|
[:a.bg-blue-100.rounded-full.px-3 {:x-data "popper()"}
|
|
|
|
|
[:a.bg-blue-100.rounded-full.px-3 {:x-tooltip "{content: ()=>$refs.selected_tt.innerHTML, popperOptions: {strategy: 'fixed', modifiers: [{name: 'flip', options: {fallbackPlacements: ['top']}}]}, placement: 'bottom', theme: 'light', allowHTML: true, appendTo: $el}"}
|
|
|
|
|
[:span.text-left {:x-ref "source"}
|
|
|
|
|
[:span {"x-text" "value.size"}]
|
|
|
|
|
" selected"]
|
|
|
|
|
(buttons/tooltip- {:x-bind "tooltip" }
|
|
|
|
|
[:div.flex.flex-wrap.gap-4.w-64
|
|
|
|
|
[:template {:x-for "v in Array.from(value.values())"}
|
|
|
|
|
[:div.bg-green-50.rounded-full.px-3.text-ellipsis.whitespace-nowrap.shrink.overflow-hidden.text-green-800 {:x-text "lookup[v]"}]]])]]
|
|
|
|
|
[:template {:x-ref "selected_tt"}
|
|
|
|
|
[:div.flex.flex-wrap.gap-4.w-64.p-4
|
|
|
|
|
[:template {:x-for "v in Array.from(value.values())"}
|
|
|
|
|
[:div.bg-green-50.rounded-full.px-3.text-ellipsis.whitespace-nowrap.shrink.overflow-hidden.text-green-800 {:x-text "lookup[v]"}]]]]]]
|
|
|
|
|
[:template {:x-if "value.size == 0"}
|
|
|
|
|
[:span.text-left.text-gray-400 "None selected"]]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[:div {:class "w-4 h-4 ml-2 inline text-gray-500 self-center rounded-full bg-gray-100 text-gray-500"
|
|
|
|
|
"@click.prevent.stop" "value = new Set([])"
|
|
|
|
|
:x-show "value.size > 0"}
|
|
|
|
|
@@ -192,52 +174,42 @@
|
|
|
|
|
:x-ref "warning_badge"
|
|
|
|
|
:x-effect "if (value.warning) { $nextTick(()=> warning_badge.update()) }"}
|
|
|
|
|
(tags/badge- {:class "peer"} "!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[:div {:x-show "value.warning"
|
|
|
|
|
:x-ref "warning_pop"
|
|
|
|
|
:class "hidden peer-hover:block bg-red-50 dark:bg-gray-600 rounded-lg shadow-2xl w-max z-50 p-4"
|
|
|
|
|
:x-text "value.warning"}]]]])
|
|
|
|
|
:x-text "value.warning"}]]]
|
|
|
|
|
[:template {:x-ref "dropdown"}
|
|
|
|
|
[:ul.dropdown-contents {:class "bg-gray-100 dark:bg-gray-600 rounded-lg shadow-2xl w-max z-50 ring-1"
|
|
|
|
|
"@keydown.escape.prevent" "tippy.hide();"
|
|
|
|
|
:x-destroy "if ($refs.input) {$refs.input.focus();}"}
|
|
|
|
|
|
|
|
|
|
[:ul.dropdown-contents {:class "bg-gray-100 dark:bg-gray-600 rounded-lg shadow-2xl w-max z-50 ring-1"
|
|
|
|
|
"x-ref" "dropdown"
|
|
|
|
|
"@keydown.escape" "open = false;"
|
|
|
|
|
"x-transition:enter" "ease-[cubic-bezier(.3,2.3,.6,1)] duration-200"
|
|
|
|
|
"x-transition:enter-start" "!opacity-0"
|
|
|
|
|
"x-transition:enter-end" "!opacity-1"
|
|
|
|
|
"x-transition:leave" "ease-out duration-200"
|
|
|
|
|
"x-transition:leave-start" "!opacity-1"
|
|
|
|
|
"x-transition:leave-end" "!opacity-0"
|
|
|
|
|
"x-show " "open"
|
|
|
|
|
"x-trap" "open"
|
|
|
|
|
"@click.outside" "open=false;"}
|
|
|
|
|
|
|
|
|
|
[:input {:type "text"
|
|
|
|
|
:class (-> (:class params)
|
|
|
|
|
(or "")
|
|
|
|
|
(hh/add-class default-input-classes)
|
|
|
|
|
(hh/replace-wildcard ["rounded" "border"] "border-bottom bg-gray-100 rounded-t-lg w-full"))
|
|
|
|
|
"x-model" "search"
|
|
|
|
|
"placeholder" (:placeholder params)
|
|
|
|
|
"@keydown.down.prevent" "active ++; active = active >= elements.length - 1 ? elements.length - 1 : active"
|
|
|
|
|
"@keydown.up.prevent" "active --; active = active < 0 ? 0 : active"
|
|
|
|
|
"@keydown.enter.prevent.stop" "if ($data.elements[active]) { if (value.has($data.elements[active].value)) { value.delete($data.elements[active].value) } else {value.add($data.elements[active].value); lookup[$data.elements[active].value] = $data.elements[active].label} } "
|
|
|
|
|
"x-init" "$watch('search', s => { if($el.value.length > 2) {fetch(baseUrl + s).then(data=>data.json()).then(data => {elements = data; active=-1}) }})"}]
|
|
|
|
|
[:div.dropdown-options {:class "rounded-b-lg overflow-hidden"}
|
|
|
|
|
[:template {:x-for "(element, index) in elements"}
|
|
|
|
|
[:li
|
|
|
|
|
[:label {:class "px-4 py-2 flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-300 [&.active]:dark:bg-primary-700 text-gray-800 dark:text-gray-100 cursor-pointer"
|
|
|
|
|
:href "#"
|
|
|
|
|
":class" "active == index ? 'active' : ''"
|
|
|
|
|
"@mouseover" "active = index"
|
|
|
|
|
"@mouseout" "active = -1"
|
|
|
|
|
"@click.prevent" "if (value.has(element.value)) { value.delete(element.value) } else {value.add(element.value); lookup[element.value] = element.label}"
|
|
|
|
|
}
|
|
|
|
|
[ :input {:type "checkbox" ":checked" "value.has(element.value)"} ]
|
|
|
|
|
[:span {"x-html" "element.label"}]]]]
|
|
|
|
|
[:template {:x-if "elements.length == 0"}
|
|
|
|
|
[:li {:class "px-4 py-2 flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-500 text-gray-800 dark:text-gray-100 text-xs "}
|
|
|
|
|
"No results found"]]]]])
|
|
|
|
|
[:input {:type "text"
|
|
|
|
|
:class (-> (:class params)
|
|
|
|
|
(or "")
|
|
|
|
|
(hh/add-class default-input-classes)
|
|
|
|
|
(hh/replace-wildcard ["rounded" "border"] "border-bottom bg-gray-100 rounded-t-lg w-full"))
|
|
|
|
|
"x-model" "search"
|
|
|
|
|
"placeholder" (:placeholder params)
|
|
|
|
|
"@keydown.down.prevent" "active ++; active = active >= elements.length - 1 ? elements.length - 1 : active"
|
|
|
|
|
"@keydown.up.prevent" "active --; active = active < 0 ? 0 : active"
|
|
|
|
|
"@keydown.enter.prevent.stop" "if ($data.elements[active]) { if (value.has($data.elements[active].value)) { value.delete($data.elements[active].value) } else {value.add($data.elements[active].value); lookup[$data.elements[active].value] = $data.elements[active].label} } "
|
|
|
|
|
"x-init" " $el.focus(); $watch('search', s => { if($el.value.length > 2) {fetch(baseUrl + s).then(data=>data.json()).then(data => {elements = data; active=-1}) }})"}]
|
|
|
|
|
[:div.dropdown-options {:class "rounded-b-lg overflow-hidden"}
|
|
|
|
|
[:template {:x-for "(element, index) in elements"}
|
|
|
|
|
[:li
|
|
|
|
|
[:label {:class "px-4 py-2 flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-300 [&.active]:dark:bg-primary-700 text-gray-800 dark:text-gray-100 cursor-pointer"
|
|
|
|
|
:href "#"
|
|
|
|
|
":class" "active == index ? 'active' : ''"
|
|
|
|
|
"@mouseover" "active = index"
|
|
|
|
|
"@mouseout" "active = -1"
|
|
|
|
|
"@click.prevent" "if (value.has(element.value)) { value.delete(element.value) } else {value.add(element.value); lookup[element.value] = element.label}"}
|
|
|
|
|
[:input {:type "checkbox" ":checked" "value.has(element.value)"}]
|
|
|
|
|
[:span {"x-html" "element.label"}]]]]
|
|
|
|
|
[:template {:x-if "elements.length == 0"}
|
|
|
|
|
[:li {:class "px-4 py-2 flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-500 text-gray-800 dark:text-gray-100 text-xs "}
|
|
|
|
|
"No results found"]]]]]])])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defn use-size [size]
|
|
|
|
|
|