This commit is contained in:
2024-10-15 19:22:19 -07:00
parent 631ee905da
commit ca27fcdb57
8 changed files with 104 additions and 197 deletions

View File

@@ -271,7 +271,7 @@
"limit" 300}))))
valid-clients (for [n name-like-ids
:when (valid-client-ids n)]
{"value" n "label" (pull-attr (dc/db conn) :client/name n)})]
{"value" n "label" (pull-attr (dc/db conn) :client/name n) "warning" "this is bad"})]
{:body (take 10 valid-clients)}))
(def search (wrap-json-response search))

View File

@@ -24,11 +24,10 @@
:class (fn [c] (-> c
(or "")
(hh/add-class "w-full p-4 modal-card flex max-h-[inherit]"))))
[:div {:class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content w-full flex flex-col max-h-full overflow-hidden"}
[:div {:class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content w-full flex flex-col max-h-full overflow-hidden" }
[:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600 shrink-0"} header]
[:div {:class "px-6 py-2 space-y-6 overflow-y-scroll w-full contents"}
#_[:div.bg-green-300.w-full.h-64
"hello"]
[:div {:class "px-6 py-2 space-y-6 overflow-y-scroll w-full shrink"}
content]
(when footer [:div {:class "p-4"}
[:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex (hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"})

View File

@@ -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]

View File

@@ -21,9 +21,9 @@
children))
(defn badge- [params & children]
[:div {:class (-> (hh/add-class "absolute inline-flex items-center justify-center w-6 h-6 text-xs font-black text-white
[:div (merge params {:class (-> (hh/add-class "absolute inline-flex items-center justify-center w-6 h-6 text-xs font-black text-white
border-3 border-white rounded-full -top-2 -right-2 dark:border-gray-900"
(:class params)
)
(hh/add-class (or (some-> (:color params) (#(str "bg-" % "-300")))
"bg-red-300")))} children])
(:class params)
)
(hh/add-class (or (some-> (:color params) (#(str "bg-" % "-300")))
"bg-red-300")))}) children])

View File

@@ -30,7 +30,6 @@
[:link {:rel "stylesheet", :href "/output.css"}]
[:script {:src "https://unpkg.com/hyperscript.org@0.9.7/dist/_hyperscript.min.js"}]
[:script {:src "https://unpkg.com/@popperjs/core@2.11.8/dist/umd/popper.min.js"}]
[:script {:src "https://cdn.plaid.com/link/v2/stable/link-initialize.js"}]
[:script { :src "https://cdn.jsdelivr.net/npm/@ryangjchandler/alpine-tooltip@1.x.x/dist/cdn.min.js" :defer true}]
[:link {:rel "stylesheet" :href "https://unpkg.com/tippy.js@6/dist/tippy.css"}]
@@ -49,9 +48,6 @@
[:script {:type "text/javascript", :src "https://cdn.yodlee.com/fastlink/v4/initialize.js", :async "async"}]]
[:link {:rel "stylesheet" :href "https://cdn.jsdelivr.net/npm/vanillajs-datepicker@1.1.4/dist/css/datepicker.min.css"}]
[:script {:type "text/javascript" :src "https://cdn.jsdelivr.net/npm/vanillajs-datepicker@1.1.4/dist/js/datepicker-full.min.js"}]
[:link {:rel "stylesheet" :href "https://cdn.jsdelivr.net/npm/choices.js@9.0.1/public/assets/styles/choices.min.css"}]
[:script {:src "https://cdn.jsdelivr.net/npm/choices.js@9.0.1/public/assets/scripts/choices.min.js"}]
[:script {:src "https://unpkg.com/htmx.org/dist/ext/response-targets.js"}]
[:script {:src "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js" :integrity "sha512-CQBWl4fJHWbryGE+Pc7UAxWMUMNMWzWxF4SQo9CgkJIN1kx6djDQZjh3Y8SZ1d+6I+1zze6Z7kHXO7q3UyZAWw==" :crossorigin "anonymous" :referrerpolicy "no-referrer"}]

View File

@@ -30,7 +30,7 @@
"fields" "id"
"limit" 300})))
valid-clients (for [n name-like-ids]
{"value" n "label" (pull-attr (dc/db conn) :vendor/name n)})]
{"value" n "label" (pull-attr (dc/db conn) :vendor/name n)} )]
{:body (take 10 valid-clients)}))
(def search (wrap-json-response search))