Skip to content

Commit

Permalink
cleaner impl of ?field persistence
Browse files Browse the repository at this point in the history
  • Loading branch information
mhuebert committed Dec 26, 2023
1 parent c8b5f33 commit 7dc370a
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 140 deletions.
6 changes: 2 additions & 4 deletions src/sb/app.cljc
Expand Up @@ -52,11 +52,9 @@
:validators [(io/min-length 8)]}
:entity/title {:validators [(io/min-length 3)]}
:board/project-fields {:view field.admin-ui/fields-editor
:make-field fields-editor-field
:props {:wrap u/prune}}
:make-field fields-editor-field}
:board/member-fields {:view field.admin-ui/fields-editor
:make-field fields-editor-field
:props {:wrap u/prune}}
:make-field fields-editor-field}
:field/label {:view field.ui/text-field}
:field/hint {:view field.ui/text-field}
:field/options {:view field.admin-ui/options-editor}
Expand Down
4 changes: 2 additions & 2 deletions src/sb/app/board/admin_ui.cljc
Expand Up @@ -35,8 +35,8 @@

[:div.field-label (t :tr/projects-and-members)]
[:div.flex-v.gap-4
(use-persisted-attr board :board/member-fields #_{:wrap u/prune})
(use-persisted-attr board :board/project-fields #_{:wrap u/prune})]
(use-persisted-attr board :board/member-fields)
(use-persisted-attr board :board/project-fields)]


[:div.field-label (t :tr/registration)]
Expand Down
8 changes: 4 additions & 4 deletions src/sb/app/domain_name/ui.cljc
Expand Up @@ -29,16 +29,16 @@
[:div.field-wrapper
[form.ui/show-label ?domain]
[:div.flex.gap-2.items-stretch
(field.ui/text-field ?domain (merge {:wrap (fn [v]
(field.ui/text-field ?domain (merge props
{:wrap (fn [v]
(when-not (str/blank? v)
{:domain-name/name (data/qualify-domain (data/normalize-domain v))}))
:unwrap (fn [v]
(or (some-> v :domain-name/name data/unqualify-domain) ""))
:auto-complete "off"
:spell-check false
:wrapper-class "flex-auto"}
props
{:label false}))
:wrapper-class "flex-auto"
:label false}))
[:div.flex.items-center.text-sm.text-gray-500.h-10 ".sparkboard.com"]]])

(defn validators []
Expand Down
23 changes: 19 additions & 4 deletions src/sb/app/entity/data.cljc
Expand Up @@ -4,7 +4,8 @@
[sb.authorize :as az]
[sb.query :as q]
[sb.schema :as sch :refer [? s- unique-uuid]]
[sb.validate :as validate]))
[sb.validate :as validate]
[inside-out.forms :as io]))

(sch/register!
(merge
Expand Down Expand Up @@ -46,7 +47,7 @@
:asset
:chat.message]}
:entity/draft? {:doc "Entity is not yet published - visible only to creator/team"
s- :boolean}
s- :boolean}
:entity/description {:doc "Description of an entity (for card/header display)"
s- :prose/as-map
#_#_:db/fulltext true}
Expand Down Expand Up @@ -107,8 +108,8 @@
{:txs txs}))

(defn persisted-value [?field]
(if-let [{:keys [entity attribute wrap]} (:field/persistence ?field)]
(wrap (get entity attribute))
(if-let [{:keys [db/id attribute]} (when (:field/persisted? ?field) ?field)]
(get (db/entity id) attribute)
(:init ?field)
#_(throw-no-persistence! ?field)))

Expand All @@ -117,6 +118,20 @@
[ctx e a v]
(save-attributes! ctx e {a v}))

(defn save-field [?field]
(when-let [{:as ?persisted-field :keys [db/id attribute]} (io/ancestor-by ?field :field/persisted?)]
(io/try-submit+ ?persisted-field
(save-attribute! nil id attribute @?persisted-field))))

(defn maybe-save-field
[?field]
(let [value @?field]
(if (and (io/closest ?field :field/persisted?)
(not= value (persisted-value ?field)))
(io/try-submit+ ?field
(save-field ?field))
(prn :not-saving value))))

(defn reverse-attr [a]
(keyword (namespace a) (str "_" (name a))))

Expand Down
39 changes: 12 additions & 27 deletions src/sb/app/entity/ui.cljc
Expand Up @@ -35,51 +35,36 @@

(def persisted-value data/persisted-value)

(defn save-field [?field & {:as props}]
(if-let [{:keys [entity attribute wrap]} (io/closest ?field :field/persistence)]
(io/try-submit+ ?field
(data/save-attribute! nil
(sch/wrap-id entity)
attribute
(wrap @?field)))
(throw-no-persistence! ?field)))

(defn view-field [?field & [props]]
(let [view (or (:view props)
(:view ?field)
(some-> (:attribute ?field) infer-view)
(throw (ex-info (str "No view declared for field: " (:sym ?field) (:attribute ?field)) {:sym (:sym ?field)
:attribute (:attribute ?field)})))
{:keys [entity attribute]} ?field]
:attribute (:attribute ?field)})))]
[view ?field (merge (:props ?field)
{:persisted-value (persisted-value ?field)
:on-save (partial save-field ?field props)}
(dissoc props :view))]))

(defn add-meta! [?field m]
(swap! (io/!meta ?field) merge
(when-let [attr (:attribute m)]
(io/global-meta attr))
m)
m)
(when-some [init (:init m)] (reset! ?field init))
?field)

(defn use-persisted-attr [e a & {:as props}]
#?(:cljs
(let [persisted-value (get e a)
make-field (or (:make-field props)
(:make-field (io/global-meta a))
#(io/field))
?field (h/use-memo #(doto (make-field)
(add-meta! {:init persisted-value
:attribute a
:entity e
:wrap (:wrap props identity)
:field/persistence {:attribute a
:entity e
:wrap (:wrap props identity)}}))
;; create a new field when the persisted value changes
(h/use-deps persisted-value))]
make-field (or (:make-field props)
(:make-field (io/global-meta a))
#(io/field))
?field (h/use-memo #(doto (make-field)
(add-meta! {:init persisted-value
:attribute a
:db/id (sch/wrap-id e)
:field/persisted? true}))
;; create a new field when the persisted value changes
(h/use-deps persisted-value))]
(view-field ?field props))))

#?(:cljs
Expand Down
56 changes: 26 additions & 30 deletions src/sb/app/field/admin_ui.cljc
Expand Up @@ -4,6 +4,7 @@
[inside-out.forms :as io]
[promesa.core :as p]
[sb.app.entity.ui :as entity.ui :refer [view-field]]
[sb.app.entity.data :as entity.data]
[sb.app.field.ui :as field.ui]
[sb.app.field.data :as data]
[sb.app.form.ui :as form.ui]
Expand Down Expand Up @@ -44,7 +45,8 @@
(io/swap-many! ?parent re-order
(get ?parent source)
side
(get ?parent destination)))
(get ?parent destination))
(entity.data/save-field ?child))
transfer-data (fn [e data]
(j/call-in e [:dataTransfer :setData] (str group)
(pr-str data)))
Expand All @@ -53,7 +55,6 @@
(try
(ui/read-string (j/call-in e [:dataTransfer :getData] (str group)))
(catch js/Error e nil)))

data-matches? (fn [e]
(some #{(str group)} (j/get-in e [:dataTransfer :types])))
[active-drag set-drag!] (h/use-state nil)
Expand Down Expand Up @@ -95,7 +96,7 @@
:before "top-[-2px]"
:after "bottom-[-2px]" nil)]}]))})))

(ui/defview show-option [{:as ?option :syms [?label ?value ?color]} {:keys [on-save]}]
(ui/defview show-option [{:as ?option :syms [?label ?value ?color]}]
(let [{:keys [drag-handle-props drag-subject-props drop-indicator]} (orderable-props ?option)]
[:div.flex.gap-2.items-center.group.relative.-ml-6.py-1
(merge {:key @?value}
Expand All @@ -109,46 +110,45 @@
"cursor-drag"]})
[icons/drag-dots]]]
[field.ui/text-field ?label {:label false
:on-save on-save
:wrapper-class "flex-auto"
:class "rounded-sm relative focus:z-2"
:style {:background-color @?color
:color (color/contrasting-text-color @?color)}}]
[:div.relative.w-10.focus-within-ring.rounded.overflow-hidden.self-stretch
[field.ui/color-field ?color {:on-save on-save
:style {:top -10
[field.ui/color-field ?color {:style {:top -10
:left -10
:width 100
:height 100
:position "absolute"}}]]
[radix/dropdown-menu {:id :field-option
:trigger [:button.p-1.relative.icon-gray.cursor-default
:trigger [:button.p-1.relative.icon-gray.cursor-default.rounded.hover:bg-gray-200.self-stretch
[icons/ellipsis-horizontal "w-4 h-4"]]
:children [[{:on-select (fn [_]
(radix/simple-alert! {:message "Are you sure you want to remove this?"
:confirm-text (t :tr/remove)
:confirm-fn (fn []
(io/remove-many! ?option)
(p/do (on-save)
(p/do (entity.data/save-field ?option)
(radix/close-alert!)))}))}
(t :tr/remove)]]}]]))

(ui/defview options-editor [?options {:keys [on-save]}]
(ui/defview options-editor [?options]
[:div.col-span-2.flex-v.gap-3
[:label.field-label (t :tr/options)]
(when (:loading? ?options)
[:div.loading-bar.absolute.h-1.top-0.left-0.right-0])
(into [:div.flex-v]
(map #(show-option % {:on-save on-save}) ?options))

(map show-option ?options))
(let [?new (h/use-memo #(io/field :init ""))]
[:form.flex.gap-2 {:on-submit (fn [^js e]
(.preventDefault e)
(io/add-many! ?options {'?value (str (random-uuid))
'?label @?new
'?color "#ffffff"})
(reset! ?new (:init ?new))
(io/try-submit+ ?new (on-save)))}
(io/try-submit+ ?new
(p/let [result (entity.data/save-field ?options)]
(reset! ?new (:init ?new))
result)))}
[field.ui/text-field ?new {:placeholder "Option label" :wrapper-class "flex-auto"}]
[:div.btn.bg-white.px-3.py-1.shadow "Add Option"]])
#_[ui/pprinted @?options]])
Expand All @@ -160,10 +160,9 @@
?required?
?show-as-filter?
?show-at-registration?
?show-on-card?]}
props]
(let [view-field (fn [?field & [more-props]]
(view-field ?field (merge props more-props)))]
?show-on-card?]}]
(let [view-field (fn [?field & [props]]
(view-field ?field props))]
[:div.bg-gray-100.gap-3.grid.grid-cols-2.pl-12.pr-7.pt-4.pb-6

[:div.col-span-2.flex-v.gap-3
Expand All @@ -182,20 +181,19 @@
(view-field ?show-at-registration?))
(view-field ?show-on-card?)
[:div
[:a.p-1.text-sm.cursor-pointer.inline-flex.gap-2.rounded.hover:bg-gray-200
[:a.p-1.-ml-1.-mt-1.text-sm.cursor-pointer.inline-flex.gap-2.rounded.hover:bg-gray-200
{:on-click #(radix/simple-alert! {:message "Are you sure you want to remove this?"
:confirm-text (t :tr/remove)
:confirm-fn (fn []
(io/remove-many! ?field)
((:on-save props)))})}
[icons/trash "text-destructive -ml-1"]
(entity.data/save-field ?field))})}
[:div.w-5.h-5.rounded.flex.items-center.justify-center.text-destructive [icons/trash "w-4 h-4"]]
(t :tr/remove)]]]]))

(ui/defview field-row
{:key (fn [{:syms [?id]} _] @?id)}
[?field {:keys [expanded?
toggle-expand!
on-save]}]
toggle-expand!]}]
(let [{:syms [?type ?label]} ?field
{:keys [icon]} (data/field-types @?type)
{:keys [drag-handle-props
Expand Down Expand Up @@ -224,13 +222,12 @@
[:div.flex.items-center.group-hover:text-black.text-gray-500.pl-2
[icons/chevron-down:mini (str "w-4" (when expanded? " rotate-180"))]]]
(when expanded?
(field-row-detail ?field {:on-save on-save}))]))
(field-row-detail ?field))]))

(ui/defview fields-editor [{:as ?fields :keys [label]} props]
(let [!new-field (h/use-state nil)
!autofocus-ref (ui/use-autofocus-ref)
[expanded expand!] (h/use-state nil)
on-save #(do (prn :will-save @?fields) (entity.ui/save-field ?fields props))]
[expanded expand!] (h/use-state nil)]
[:div.field-wrapper {:class "labels-semibold"}
[:label.field-label {:class "flex items-center"}
label
Expand All @@ -251,10 +248,9 @@
(->> ?fields
(map (fn [{:as ?field :syms [?id]}]
(field-row ?field
(merge {:expanded? (= expanded @?id)
:toggle-expand! #(expand! (fn [old]
(u/guard @?id (partial not= old))))
:on-save on-save}))))
{:expanded? (= expanded @?id)
:toggle-expand! #(expand! (fn [old]
(u/guard @?id (partial not= old))))})))
doall)]
(when-let [{:as ?new-field
:syms [?type ?label]} @!new-field]
Expand All @@ -265,7 +261,7 @@
(io/add-many! ?fields @?new-field)
(expand! (:field/id @?new-field))
(reset! !new-field nil)
(on-save)))}
(entity.data/save-field ?fields)))}
[:div.h-10.flex.items-center [(:icon (data/field-types @?type)) "icon-lg text-gray-700 mx-2"]]
[field.ui/text-field ?label {:label false
:ref !autofocus-ref
Expand Down

0 comments on commit 7dc370a

Please sign in to comment.