From ffcc43ba5e5894fa6ca30293100d6dcfe58138af Mon Sep 17 00:00:00 2001 From: Bryce Date: Fri, 20 Oct 2023 17:52:15 -0700 Subject: [PATCH] alpine for dropdown. --- resources/public/output.css | 85 +++++++++++++++++++ .../auto_ap/ssr/admin/transaction_rules.clj | 49 ++++++----- src/clj/auto_ap/ssr/company.clj | 19 ++--- src/clj/auto_ap/ssr/components.clj | 1 + src/clj/auto_ap/ssr/components/dialog.clj | 6 +- src/clj/auto_ap/ssr/components/inputs.clj | 70 ++++++++++++++- src/clj/auto_ap/ssr/svg.clj | 2 +- src/clj/auto_ap/ssr/ui.clj | 32 ++++--- 8 files changed, 215 insertions(+), 49 deletions(-) diff --git a/resources/public/output.css b/resources/public/output.css index 5029ff16..fc47979a 100644 --- a/resources/public/output.css +++ b/resources/public/output.css @@ -1252,6 +1252,26 @@ input:checked + .toggle-bg { margin-top: 1.25rem; } +.\!mt-0 { + margin-top: 0px !important; +} + +.\!mt-3 { + margin-top: 0.75rem !important; +} + +.mt-3 { + margin-top: 0.75rem; +} + +.\!mt-1 { + margin-top: 0.25rem !important; +} + +.ml-64 { + margin-left: 16rem; +} + .block { display: block; } @@ -1408,6 +1428,11 @@ input:checked + .toggle-bg { width: auto; } +.w-max { + width: -moz-max-content; + width: max-content; +} + .max-w-2xl { max-width: 42rem; } @@ -1452,6 +1477,10 @@ input:checked + .toggle-bg { flex-shrink: 0; } +.flex-grow { + flex-grow: 1; +} + .basis-1\/4 { flex-basis: 25%; } @@ -1622,6 +1651,10 @@ input:checked + .toggle-bg { justify-content: space-between; } +.justify-items-stretch { + justify-items: stretch; +} + .gap-1 { gap: 0.25rem; } @@ -1714,6 +1747,22 @@ input:checked + .toggle-bg { border-color: rgb(243 244 246 / var(--tw-divide-opacity)); } +.place-self-start { + place-self: start; +} + +.place-self-end { + place-self: end; +} + +.justify-self-start { + justify-self: start; +} + +.justify-self-end { + justify-self: end; +} + .overflow-auto { overflow: auto; } @@ -2326,6 +2375,10 @@ input:checked + .toggle-bg { opacity: 1; } +.\!opacity-0 { + opacity: 0 !important; +} + .shadow { --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); @@ -2354,6 +2407,10 @@ input:checked + .toggle-bg { outline-style: solid; } +.outline-0 { + outline-width: 0px; +} + .ring { --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color); @@ -2411,6 +2468,10 @@ input:checked + .toggle-bg { transition-duration: 75ms; } +.duration-200 { + transition-duration: 200ms; +} + .ease-in-out { transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } @@ -2419,6 +2480,10 @@ input:checked + .toggle-bg { transition-timing-function: cubic-bezier(0, 0, 0.2, 1); } +.ease-\[cubic-bezier\(\.3\2c 2\.3\2c \.6\2c 1\)\] { + transition-timing-function: cubic-bezier(.3,2.3,.6,1); +} + .htmx-added .fade-in { opacity: 0.0 !important; } @@ -2862,6 +2927,11 @@ input:checked + .toggle-bg { background-color: rgb(255 255 255 / var(--tw-bg-opacity)); } +.hover\:bg-neutral-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(245 245 245 / var(--tw-bg-opacity)); +} + .hover\:text-blue-600:hover { --tw-text-opacity: 1; color: rgb(0 125 187 / var(--tw-text-opacity)); @@ -2910,6 +2980,11 @@ input:checked + .toggle-bg { border-color: rgb(121 181 46 / var(--tw-border-opacity)); } +.focus\:bg-neutral-100:focus { + --tw-bg-opacity: 1; + background-color: rgb(245 245 245 / var(--tw-bg-opacity)); +} + .focus\:text-green-700:focus { --tw-text-opacity: 1; color: rgb(73 109 28 / var(--tw-text-opacity)); @@ -3524,3 +3599,13 @@ input:checked + .toggle-bg { padding-left: 16rem; } } + +.\[\&\.active\]\:bg-red-500.active { + --tw-bg-opacity: 1; + background-color: rgb(255 3 3 / var(--tw-bg-opacity)); +} + +.\[\&\.active\]\:bg-primary-500.active { + --tw-bg-opacity: 1; + background-color: rgb(121 181 46 / var(--tw-bg-opacity)); +} diff --git a/src/clj/auto_ap/ssr/admin/transaction_rules.clj b/src/clj/auto_ap/ssr/admin/transaction_rules.clj index 01fcbdca..d7b0cc0d 100644 --- a/src/clj/auto_ap/ssr/admin/transaction_rules.clj +++ b/src/clj/auto_ap/ssr/admin/transaction_rules.clj @@ -306,7 +306,7 @@ (or (get tempids (:db/id entity)) (:db/id entity)))] (html-response (row* identity updated-account {:flash? true}) - :headers {"hx-trigger" "modalClosing" + :headers {"hx-trigger" "modalclose" "hx-retarget" (format "#transaction-rule-table tr[data-id=\"%d\"]" (:db/id updated-account))}))) @@ -330,7 +330,7 @@ (defn- account-typeahead* [{:keys [name value client-id]}] [:div.flex.flex-col - (com/typeahead {:name name + (com/typeahead-2 {:name name :placeholder "Search..." :url (str (bidi/path-for ssr-routes/only-routes :account-search) "?client-id=" client-id) :id name @@ -408,6 +408,7 @@ :href "#"} svg/x)))) +;; TODO dialog is no longer closeable (defn dialog* [& {:keys [ entity form-params form-errors]}] (com/modal {:modal-class "max-w-4xl"} @@ -417,7 +418,8 @@ [:form#edit-form (merge {:hx-ext "response-targets" :hx-swap "outerHTML swap:300ms" :hx-target "#modal-holder" - :hx-target-400 "#form-errors .error-content"} + :hx-target-400 "#form-errors .error-content" + #_#_:x-trap "true"} form-params) [:fieldset {:class "hx-disable" :hx-disinherit "hx-target"} @@ -433,17 +435,18 @@ {:label "Client" :errors (fc/field-errors)} [:div.w-96 - (com/typeahead {:name (fc/field-name) - :error? (fc/error?) - :class "w-96" - :placeholder "Search..." - :url (bidi/path-for ssr-routes/only-routes :company-search) - :id (str "form-client-search") - :value (fc/field-value) - :value-fn (some-fn :db/id identity) - :content-fn (fn [c] (cond->> c - (nat-int? c) (dc/pull (dc/db conn) '[:client/name]) - true :client/name))})])) + (com/typeahead-2 {:name (fc/field-name) + :x-init "$el.focus()" + :error? (fc/error?) + :class "w-96" + :placeholder "Search..." + :url (bidi/path-for ssr-routes/only-routes :company-search) + :id (str "form-client-search") + :value (fc/field-value) + :value-fn (some-fn :db/id identity) + :content-fn (fn [c] (cond->> c + (nat-int? c) (dc/pull (dc/db conn) '[:client/name]) + true :client/name))})])) (fc/with-field :transaction-rule/bank-account @@ -510,13 +513,13 @@ (com/validated-field {:label "Assign Vendor" :errors (fc/field-errors)} [:div.w-96 - (com/typeahead {:name (fc/field-name) - :placeholder "Search..." - :url (bidi/path-for ssr-routes/only-routes :vendor-search) - :id (str "form-vendor-search") - :value (fc/field-value) - :value-fn (some-fn :db/id identity) - :content-fn (some-fn :vendor/name #(pull-attr (dc/db conn) :vendor/name %))})])) + (com/typeahead-2 {:name (fc/field-name) + :placeholder "Search..." + :url (bidi/path-for ssr-routes/only-routes :vendor-search) + :id (str "form-vendor-search") + :value (fc/field-value) + :value-fn (some-fn :db/id identity) + :content-fn (some-fn :vendor/name #(pull-attr (dc/db conn) :vendor/name %))})])) (fc/with-field :transaction-rule/accounts (list @@ -609,7 +612,7 @@ (html-response (dialog* :entity entity :form-params {:hx-put (str (bidi/path-for ssr-routes/only-routes :admin-transaction-rule-edit-save))}) - :headers {"hx-trigger" "modalOpening"}))) + :headers {"hx-trigger" "modalopen"}))) (defn transaction-rule-error [request] (let [entity (some-> request :last-form)] @@ -629,7 +632,7 @@ :form-errors {} :form-params {:hx-post (str (bidi/path-for ssr-routes/only-routes :admin-transaction-rule-edit-save))}) - :headers {"hx-trigger" "modalOpening"})) + :headers {"hx-trigger" "modalopen"})) (def transaction-rule-schema (mc/schema [:map diff --git a/src/clj/auto_ap/ssr/company.clj b/src/clj/auto_ap/ssr/company.clj index 3891bb3a..075bae63 100644 --- a/src/clj/auto_ap/ssr/company.clj +++ b/src/clj/auto_ap/ssr/company.clj @@ -95,7 +95,7 @@ selected-client-id) :client/bank-accounts (filter (fn [{:keys [bank-account/name]}] - (str/includes? (str/upper-case name) + (str/includes? (or (some-> name str/upper-case) "") (or (some-> query-params (get "q") str/upper-case) @@ -108,15 +108,14 @@ (defn bank-account-typeahead* [{:keys [client-id name value]}] (if client-id - (com/typeahead {:name name - :class "w-96" - :placeholder "Search..." - :url (bidi/path-for ssr-routes/only-routes :bank-account-search - :db/id client-id) - :id (str "form-bank-account-search") - :value value - :value-fn (some-fn :db/id identity) - :content-fn (some-fn :bank-account/name #(pull-attr (dc/db conn) :bank-account/name %))}) + (com/typeahead-2 {:name name + :class "w-96" + :placeholder "Search..." + :url (bidi/path-for ssr-routes/only-routes :bank-account-search + :db/id client-id) + :value value + :value-fn (some-fn :db/id identity) + :content-fn (some-fn :bank-account/name #(pull-attr (dc/db conn) :bank-account/name %))}) [:span.text-xs.text-gray-500 "Please select a client before selecting a bank account." [:input {:type "hidden" :name name}]])) diff --git a/src/clj/auto_ap/ssr/components.clj b/src/clj/auto_ap/ssr/components.clj index e7f6782d..41fff258 100644 --- a/src/clj/auto_ap/ssr/components.clj +++ b/src/clj/auto_ap/ssr/components.clj @@ -31,6 +31,7 @@ (def hidden inputs/hidden-) (def select inputs/select-) (def typeahead inputs/typeahead-) +(def typeahead-2 inputs/typeahead-2-) (def field-errors inputs/field-errors-) (def field inputs/field-) (def validated-field inputs/validated-field-) diff --git a/src/clj/auto_ap/ssr/components/dialog.clj b/src/clj/auto_ap/ssr/components/dialog.clj index e6e2e99e..4087c83c 100644 --- a/src/clj/auto_ap/ssr/components/dialog.clj +++ b/src/clj/auto_ap/ssr/components/dialog.clj @@ -2,10 +2,10 @@ (:require [hiccup2.core :as hiccup])) (defn modal- [params & children] - [:div {:class (str "relative w-full max-h-full " (or (:modal-class params) " max-w-2xl "))} + [:div {:class (str "relative w-full max-h-full " (or (:modal-class params) " max-w-2xl ")) + "@click.outside" "open=false"} (into [:div#modal-content] - children)] - ) + children)]) (defn modal-card- [params header content footer] [:div#modal-card params diff --git a/src/clj/auto_ap/ssr/components/inputs.clj b/src/clj/auto_ap/ssr/components/inputs.clj index 02678df7..a19199e6 100644 --- a/src/clj/auto_ap/ssr/components/inputs.clj +++ b/src/clj/auto_ap/ssr/components/inputs.clj @@ -2,7 +2,9 @@ (:require [hiccup2.core :as hiccup] [auto-ap.ssr.hiccup-helper :as hh] - [clojure.string :as str])) + [clojure.string :as str] + [auto-ap.ssr.svg :as svg] + [auto-ap.ssr.hx :as hx])) (def default-input-classes @@ -68,6 +70,72 @@ c.clearChoices(); (:url params) ))]]) +(defn typeahead-2- [params] + [:div {:x-data (hx/json {:open false + :baseUrl (if (str/includes? (:url params) "?") + (str (:url params) "&q=") + (str (:url params) "?q=")) + :value {:value ((:value-fn params first) (:value params)) :label ((:content-fn params second) (:value params))} + :search "" + :active 0 + :elements (if (:value params) + [{:value ((:value-fn params first) (:value params)) :label ((:content-fn params second) (:value params))}] + [])}) + } + [:a {:class (-> (hh/add-class (or (:class params) "") default-input-classes) + (hh/add-class "cursor-pointer")) + "@click.prevent.stop" "open = !open;" + "@keydown.enter.prevent.stop" "open = !open;" + :x-init (:x-init params) + :tabindex 0} + [:input (-> params + (dissoc :class) + (dissoc :value-fn) + (dissoc :content-fn) + (dissoc :x-init) + (dissoc :placeholder) + (assoc + "x-ref" "hidden" + :type "hidden" + ":value" "value.value" + :x-init "$watch('value', v => $dispatch('change'))"))] + [:div.flex.w-full.justify-items-stretch + [:span.flex-grow.text-left {"x-text" "value.label"}] + [:div {:class "w-5 h-5 inline ml-1 justify-self-end"} + svg/drop-down]]] + + [:ul.dropdown-contents {:class "absolute bg-white rounded-lg shadow-lg py-1 w-max z-10 mt-1" + "@keydown.escape.window" "open = false" + "x-transition:enter" "ease-[cubic-bezier(.3,2.3,.6,1)] duration-200" + "x-transition:enter-start" "!opacity-0 !mt-0" + "x-transition:enter-end" "!opacity-1 !mt-1" + "x-transition:leave" "ease-out duration-200" + "x-transition:leave-start" "!opacity-1 !mt-1" + "x-transition:leave-end" "!opacity-0 !mt-0" + "x-show ""open" + "x-trap" "open" + "@click.outside" "open=false"} + [:input {:type "text" :class (hh/add-class (or (:class params) "") default-input-classes) + "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" "open = false; value = elements.length > 0 ? $data.elements[active] : {'value': '', label: ''}; console.log('are we here')" + "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 + [: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-500" + :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-500 text-xs "} + "No results found"]]]]]) + (defn use-size [size] (if (= :small size) diff --git a/src/clj/auto_ap/ssr/svg.clj b/src/clj/auto_ap/ssr/svg.clj index 385bbc1b..077f0988 100644 --- a/src/clj/auto_ap/ssr/svg.clj +++ b/src/clj/auto_ap/ssr/svg.clj @@ -200,7 +200,7 @@ :stroke-linejoin "round"}]]) (def drop-down - [:svg {:class "w-4 h-4 ml-2", :aria-hidden "true", :fill "none", :stroke "currentColor", :viewbox "0 0 24 24", :xmlns "http://www.w3.org/2000/svg"} + [:svg {:aria-hidden "true", :fill "none", :stroke "currentColor", :viewbox "0 0 24 24", :xmlns "http://www.w3.org/2000/svg"} [:path {:stroke-linecap "round", :stroke-linejoin "round", :stroke-width "2", :d "M19 9l-7 7-7-7"}]]) (def download diff --git a/src/clj/auto_ap/ssr/ui.clj b/src/clj/auto_ap/ssr/ui.clj index 41ba3fe2..2564987f 100644 --- a/src/clj/auto_ap/ssr/ui.clj +++ b/src/clj/auto_ap/ssr/ui.clj @@ -1,6 +1,7 @@ (ns auto-ap.ssr.ui (:require - [hiccup2.core :as hiccup])) + [hiccup2.core :as hiccup] + [auto-ap.ssr.hx :as hx])) (defn html-page [hiccup] {:status 200 @@ -42,7 +43,8 @@ [:script {:src "https://unpkg.com/dropzone@5.9.3/dist/min/dropzone.min.js"}] [:link {:rel "stylesheet" :href "https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" :type "text/css"}] - #_[:script {:defer true :src "https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"}] + [:script {:defer true :src "https://cdn.jsdelivr.net/npm/@alpinejs/focus@3.x.x/dist/cdn.min.js"}] + [:script {:defer true :src "https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"}] [:style " input::-webkit-outer-spin-button, @@ -58,24 +60,32 @@ input[type=number] { " ] - [:body {:hx-ext "disable-submit"} + [:body {:hx-ext "disable-submit" + "x-data" (hx/json {:modalOpen false})} contents [:script {:src "/js/flowbite.min.js"}] - [:div [:div#modal-holder - { :tabindex "-1", :class "fixed top-0 left-0 right-0 z-50 w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full flex justify-center hidden" - :aria-hidden true - "_" (hiccup/raw "on \"modalClosed\" remove my children - on \"modalOpening\" from call curModal.show() - on \"modalClosing\" from call curModal.hide()") + [:div + {"x-data" (hx/json {"open" false}) + "@modalopen.document" "open=true" + "@modalclose.document" "open=false"} + + [:div#modal-holder + { :tabindex "-1", :class "fixed top-0 left-0 right-0 z-50 w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full flex justify-center" + ":class" "open ? '' : 'hidden'" + ":aria-hidden" "!open" }] - [:script {:lang "text/javascript"} + [:div {:class " bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-40" + ":class" "open ? '' : 'hidden'" + ":aria-hidden" "!open" + }] + #_[:script {:lang "text/javascript"} (hiccup/raw " var modal_element = document.getElementById('modal-holder'); var modal_options = { placement: 'center', backdrop: 'dynamic', backdropClasess: 'bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-40', - closable: true, + closable: false, onOpen: function() { htmx.trigger(document.getElementById('modal-holder'), 'modalOpened', {});