metasoarous / datview

Effortlessly compose data visualizations and controls for Datomic and DataScript data
Eclipse Public License 1.0
25 stars 5 forks source link

Formalize context flow/scoping #3

Open metasoarous opened 8 years ago

metasoarous commented 8 years ago

Feels like there could be a lot of potential patterns for how one component sets up context for children components. We should think about these possible patterns and how we can standardize around a sort of meta-pattern here to keep things sane. Is this something that can be dispatched as well?

metasoarous commented 8 years ago

Moving over discussion from #5 on scoping entities.

The vague working draft idea I have now is to build entities that have scoping data (organization of that data to be determined), as well as some scoping parameters. Things like :dat.view.scope/type, :dat.view.scope/attribute, :dat.view.scope/cardinality, etc, so that in a given component you specify any scoping parameters which might apply (the attribute, it's cardinality, the entity type, etc). The two big questions are:

  1. What are the details for how these scoping declarations work? Are they or specifications or and specifications?
  2. Given a collection of context data entities matching some scope description, how do we produce a single context entity which represents the aggregate context data? Is there just a hard coded precedence? Or can context data entities describe their own relation to other such entities in terms of precedence in some kind of sane manner?

There is some work in the dat.view.representations ns regarding context resolution, but it goes in a slightly different direction. Hopefully there's something nice in the space between.

bamarco commented 8 years ago

First thoughts. What about having some meta scope keyword that acts as a scope-channel. A scope-channel can be specified to run as or or and. A scope-channel can be marked as dependency a la plumbing or ss-component.

metasoarous commented 8 years ago

Interesting... Can you elaborate?

What if the entity just pointed to a scope query directly? Then or/and could be taken care of via datalog.

bamarco commented 8 years ago

I'm not sure how you would point to the query directly.

My idea in query form:

[:find (pull ?chan) (pull ?scope-entity)
 :in $ ?data-entity
 :where
 [?scope :scope/type ?type]
 [?scope :scope/attr ?attr]
 [?scope :scope/chan ?chan]
 ;;[?chan :scope.chan/index ?chan-order]
 ;;[?chan :scope.chan/require ?parent-chans]
 ;;[?chan :scope.chan/merge-style ?chan-style]
 [?scope :scope/entity ?scope-entity]
 [?data-entity :e.type/type ?type]
 ...]

  (let [scopes (sortedmap (GROUPBY ?chan query) #(if (child? %1 %2) -1 (if (child? %2 %1) 1 0)))]  
      ;; TODO: merge scope-entity based on ?chan-style using ?chan-order if needed (which is the order they are specified in) for each ?chan
      ;; TODO: merge ?chan based on hierarchy and ?chan-style of default-chan
    )
bamarco commented 8 years ago

I think I might see the disconnect. I'm talking about the merging strategy after a set of scope-entities have been selected and I think you're talking about how a singular scope-entity is selected from varied ands and ors performed on the schema?

bamarco commented 8 years ago

How about this pattern? 1) The base representation function creates a bunch of re-frame subscriptions. 2) Control middleware adds re-frame handlers using subscriptions and possibly adding some state. 3) Layout middleware decides where everything goes and makes choices based on layout oriented controls. Passes context.


(defn contextualize [app parent-context parent-data data]
  (let [context (assoc :parent-eid (:db/id data))]
    (reduce #(control control-info %) [representation-fn app [(representation-query app data) context] data] (controls-query app data))
    ))

(defn toggle! [data]
  (swap! data #(not %)))

(defn control [[_ {:keys [starts-collapsed?]}] representation-fn]
  ::collapsable
  (fn [app [represent-id context] data]
    (let [collapsed? (atom starts-collapsed?)]
      (assoc-in data [:controls :collapse] {:collapser #(toggle! collapsed?) :collapsed? @collapsed?}))
    [representation-fn app [represent-id context] data]))

(defn control [[_ {:keys [parent-eid]}] representation-fn]
  ::edit-field
  (fn [app [represent-id context] data]
    (let [subscription [representation-fn app [represent-id context] data]
          editor
          (case (:type data)
            :db.type/string (fn [db] [:db/add db (:parent-eid context) (:value subscription)])
            ...)]
      (assoc-in subscription [:controls :edit] editor))))

(defn layout [[_ {:keys [editable? collapsable?]}] representation-fn]
  ::collection-layout
  (fn [app context data]
    (let [subscription [representation-fn app context data]]
      [:div {:class (:name subscription)}
       (when collapsable?
         (re-com/button
           :label "<"
           :on-click (get-in subscription [:controls :collapse :collapser])))
       (when (get-in subscription [:controls :collapse :collapsed?])
         [:ul
          (for [item (:items subscription)]
            (let [item (contextualize app context data item)]
              [:li
               (layout (layout-query app item) item)
               (when editable?
                 (re-com/button
                   :label "-"
                   :on-click (get-in item [:controls :delete])))])
            )]
         (when editable?
           (re-com/button
             :label "+"
             :on-click (get-in subscription [:controls :add]))))
       ])))