Fork me on GitHub
#hoplon
<
2016-04-11
>
levitanong01:04:14

Oh snap, Bret Victor

dm312:04:21

@jumblerg: do you eschew stylesheets completely?

jumblerg12:04:56

and fishing around in the dom for things with string matching queries when you could just bind to variables instead

jumblerg12:04:05

good apis are supposed to be cohesive. external stylesheets and html smear similar concerns all over the app in different languages.

dm312:04:10

it seems that with Hoplon you can have your styles encapsulated in defelems thus avoiding the need in classes

jumblerg12:04:15

you’re supposed to separate different concerns, like presentation from data, not content from styles (whatever that means).

dm312:04:16

but then you have things like media queries

dm312:04:09

guess you can work around that by storing window size in the cell

jumblerg12:04:34

well, not media queries (unnecessary “queries” are exactly what i’m trying to get away from), but a way of doing responsive layouts.

jumblerg12:04:45

for example:

(u/button  :w ["100%" sm 200] :h 50 :click #(swap! things conj (count @things)) "Add Thing”)
the vector after :w (width) means set the width to “100%” below the screen width contained in the var sm, and 200 pixels when the screen is above it.

dm312:04:06

hm, can't say I have much experience designing responsive layouts

dm312:04:25

do you not have the need for rem/em?

jumblerg12:04:28

this is much more powerful than anything bootstrap offers

jumblerg12:04:09

they give you 4 hardcoded breakpoints that you have to deal with via stylesheets. this gives you an infinite number of them.

jumblerg12:04:20

i haven’t had a case for them yet, but you can do :w “2em” or whatever.

dm312:04:54

nowadays I use flexbox for everything

jumblerg12:04:17

i’ve done a few flexbox-only layouts, but i gave up on it. too many nasty edge cases.

jumblerg12:04:28

if there are a large number of items on a page in a flexbox layout, the performance is terrible and then theres the issue where you have a few items left over that don’t quite fill a row, and you want them aligned on one side or another.

dm312:04:07

so I understand you have your own layout model using :display table-cell?

jumblerg12:04:16

it certainly isn’t a panacea. maybe a good fit in some cases like menus.

jumblerg12:04:13

yeah, each elem in ui actually produces three divs: the outer is display inline-table, the middle table-cell, and the inner block.

jumblerg12:04:53

the outer div is used for sizing. the middle is used for stroke, borders, etc. the inner div is the thing itself, might be a button or whatever.

jumblerg12:04:50

there are a number of motivations for doing this, i can’t recount them all offhand, but one big one is the ability to include the margin in the size. say you need four items across a page, this gives you the ability to simply set the width to 25% and get what you want.

jumblerg12:04:20

margins are implemented as the padding between the middle and outer divs.

jumblerg12:04:47

it is the use of the tables and table cells that make actual vertical positioning possible.

dm312:04:40

hm, would be interesting to read a writeup

dm312:04:50

then try out replicating flexbox layouts

jumblerg12:04:21

i think this does better. the biggest feature this lib is missing is the ability to set a value like :fill, :rest, or :max on a width or height attribute to tell the browser to render the element greedily to fill up the remaining space on a line.

jumblerg12:04:32

and, uh, the manifesto is forthcoming. right now this is just a tool to help me build my app.

dm312:04:01

looks very interesting

jumblerg12:04:46

i wasn’t sure if it was going to work out for a while, but it seems to have turned a corner. i’m finding it quite practical to work with now.

alandipert13:04:22

@jumblerg: really exciting stuff

jumblerg13:04:15

yeah… and i think it will get even better as @micha tunes the hoplon level abstractions that make it possible.

alandipert13:04:12

i forgot about this thing i made, which is kinda an approach to the meaning-of-attributes discussion from the other day http://tailrecursion.com/~alan/tmp/css5000/

alandipert13:04:05

but not really since it's not general, more just a way to hide things on an object

alandipert13:04:12

one of many ways

jumblerg13:04:04

interesting

dm313:04:44

found the explanation for the Javelin cell-based system (yesterday's discussion): http://tailrecursion.com/~alan/index.cgi/doc/tip/documents/Dipert-FRP_in_ClojureScript_with_Javelin.pdf simple_smile

dm313:04:44

the only difference seems that you didn't have a distinction between input and formula cells at that point

alandipert13:04:14

@dm3: there was a distinction internally, but less so syntactically

alandipert13:04:52

since we were still high on sheets, in the same way all the boxes on a spreadsheet look the same, we thought the cell constructor should be the same regardless of whether you create input or formula

alandipert13:04:01

which turned out to be dumb lol

alandipert13:04:29

micha came up with cell= a few months after that preso and all was right

alandipert13:04:54

and then cell became a function and lined up better with atom

alandipert13:04:06

then like 2 yrs later lift => formula

thedavidmeister13:04:00

potentially dumb q

thedavidmeister13:04:20

if I have (cell= (if a b c))

thedavidmeister13:04:28

where a, b and c are all cells

thedavidmeister13:04:51

will anything happen if a doesn’t change but b and/or c does?

alandipert13:04:42

we can't know from that expr what b and/or c do, so possibly yes

alandipert13:04:57

the only thing the if determines there is the value of the outer cell=

alandipert13:04:49

an excellent question though, it took some thinking

alandipert13:04:21

does that make sense?

thedavidmeister13:04:09

i’m confused by some weird behaviour

thedavidmeister13:04:38

that i thought was due to javascript events

thedavidmeister13:04:48

but i think is from the way cells are working

thedavidmeister13:04:38

(h/defelem item-list
  [attributes children]
  (let [list-id         (:list-id attributes)
        items           (j/cell= (if  (integer? list-id)
                                      (->>
                                        (d/datoms state/conn :avet :item/list-id list-id)
                                        (map :e)
                                        (apply sorted-set)
                                        vec)
                                      []))
        items:last      (j/cell= (last items))
        items+final     (j/cell= (conj items :new))]

    (h/div
      attributes
      children
      (j/cell= (prn "items " items))

      (h/loop-tpl :bindings [id items+final]
        (item/item
          :list-id list-id
          :id id)))))

thedavidmeister13:04:50

(h/defelem item-text
  [{:keys [label list-id id key entity]} children]
  (h/label
    (j/cell= label)

    (h/input
      :type "text"
      :value (j/cell= (if-not (= :new id)
                              ((keyword key) entity)
                              ""))
      :placeholder (j/cell= label)

      :input #(set-item! @entity @list-id @id key (if-not (= "" @%) @%)))))

(h/defelem item
  [attributes children]
  (let [list-id (:list-id attributes)
        id (:id attributes)
        entity (j/cell= (if (integer? id)
                            (d/touch (d/entity state/conn id))))]

    (h/div
      :item-id (j/cell= id)

      (j/cell= (prn "entity" entity))

      (item-text
        :label "Min"
        :list-id list-id
        :id id
        :key :min
        :entity entity)

      (item-text
        :label "Max"
        :list-id list-id
        :id id
        :key :max
        :entity entity))))

thedavidmeister13:04:58

hmm, maybe that’s a bit too big...

thedavidmeister13:04:30

basically, this bit

thedavidmeister13:04:33

(j/cell= (if (integer? id)
                            (d/touch (d/entity state/conn id))))

thedavidmeister13:04:09

actually, maybe i’m doing this wrong..

thedavidmeister13:04:54

I get a prn from the state/conn changing when I transact

thedavidmeister13:04:11

but not from the entity being updated

alandipert13:04:28

hm sounds like maybe the way you get the entity, it's not in a cell that depends on the db?

dm313:04:24

is state/conn a connection or a db?

thedavidmeister13:04:24

(def conn (local-storage (du/conn-cell schema) ::conn-state))

(j/cell= (prn conn))
(j/cell= (prn "e " (d/touch (d/entity conn 2))))

thedavidmeister13:04:36

that prints out OK

alandipert13:04:00

when you do h/loop-tpl :bindings [id items+final]

thedavidmeister13:04:25

i expected to see an “entity” line just under the last “e” line with the same data

thedavidmeister13:04:59

but it only prints the first time i add a datom to the entity 😞

thedavidmeister14:04:28

which leads to some really whacky bugs

thedavidmeister14:04:42

entity (j/cell= (if (integer? id)
                            (d/touch (d/entity state/conn id))))
@alandipert: the state/conn is a cell, so it should update entity

thedavidmeister14:04:47

i would have thought

dm314:04:00

in the simplified example above

dm314:04:19

does it print e {...} just after the first transaction?

thedavidmeister14:04:49

the screenshot above is the whole log

dm314:04:05

if you remove local storage

dm314:04:11

simplify to the max

thedavidmeister14:04:45

so i put “a” in max, got a bunch of logs as I expect

thedavidmeister14:04:13

then I put an “a” in min, only got the log in the same ns as the connection, and nothing coming from my defelems

dm314:04:02

did you print id?

dm314:04:06

is it an integer?

thedavidmeister14:04:03

no local storage

thedavidmeister14:04:35

it doesn’t even print id on the second go

thedavidmeister14:04:23

(h/loop-tpl :bindings [id items+final]
        (item/item
          :list-id list-id
          :id id)))))

thedavidmeister14:04:32

(h/defelem item
  [attributes children]
  (let [list-id (:list-id attributes)
        id (:id attributes)

dm314:04:00

it means id doesn't change

thedavidmeister14:04:09

actually, yeah, that’s right

thedavidmeister14:04:12

id doesn’t change

thedavidmeister14:04:14

but entity should

thedavidmeister14:04:26

because state/conn changed

thedavidmeister14:04:43

(j/defc a true)
(j/defc b 0)
(h/with-interval 2000 (reset! b (rand-int 10)))
(j/cell= (if a (prn b)))

thedavidmeister14:04:51

this happily prints out random integers

dm314:04:25

I really don't think the cells are broken

thedavidmeister14:04:35

entity (j/cell= (if (integer? id)
                            (do (prn "foo" (d/touch (d/entity state/conn id)))
                                (d/touch (d/entity state/conn id)))))
        bar (j/cell= (prn "id" id))
        foo (j/cell= (prn "entity" entity))]

dm314:04:38

are you sure the datoms are transacted properly?

dm314:04:11

so now it updates?

thedavidmeister14:04:37

but entity is not updating

dm314:04:54

is entity a reference value?

dm314:04:00

I haven't tried putting entities in cells

dm314:04:15

it might be a reference

dm314:04:38

depending on what you want to happen

dm314:04:52

guess it's time to look at datascript sources, no? simple_smile

thedavidmeister14:04:14

not sure what i’m looking for

dm314:04:17

today's question: "Is it a ref?"

thedavidmeister14:04:27

how could an entity break a cell=?

dm314:04:22

two entities are equal

dm314:04:30

if their eids are equal

dm314:04:49

you need to convert it into a map

dm314:04:54

if you want value semantics

thedavidmeister14:04:14

well i suppose that explains things

thedavidmeister14:04:26

don’t put entities in cell=

thedavidmeister14:04:04

of course, things broke even more when i made it a map, but at least I know roughly what is broken now >.<

dm314:04:34

you can put an entity in a cell=

dm314:04:41

but it will be the same as the id cell

thedavidmeister14:04:02

well, if i return the entity to cell=

thedavidmeister14:04:08

it will never notice that it has changed

thedavidmeister14:04:49

grr, still seeing the original bug >.<

thedavidmeister15:04:06

anyway, thanks again @dm3 and @alandipert

thedavidmeister15:04:38

is there a way to avoid the way that cell= doesn’t do anything if the value returned is the same?

thedavidmeister15:04:04

i have the situation where i have a loop of inputs

thedavidmeister15:04:25

if i delete the last character from an input, it deletes that input

thedavidmeister15:04:36

if i put “a” and “b” in the first two inputs, then delete “a”, all good, “b” is left

thedavidmeister15:04:20

if I put “a” in both the first two inputs, then delete the first “a”, i get bad times

thedavidmeister15:04:31

i’m left with two blank inputs

thedavidmeister15:04:06

it’s because :value (cell= value)

thedavidmeister15:04:40

value is “a” before and after the deletion

thedavidmeister15:04:09

so hoplon does nothing, and chrome deletes the “a” from the input

thedavidmeister15:04:36

actually there’s 4 inputs

thedavidmeister15:04:03

but this is “a, a, a, a” vs. “a, b, c, d” then deleting the first row in both cases

thedavidmeister15:04:16

i want to see “a, a” and “c, d”

micha15:04:21

(cell= value) seems redundant

micha15:04:40

why not just use value there?

thedavidmeister15:04:47

i’m simplifying for the example

thedavidmeister15:04:53

:value (j/cell= (if-not (= :new id)
                              ((keyword key) entity)
                              "”))

micha15:04:56

how are the two inputs related though?

micha15:04:03

are they sharing the same value cell?

micha15:04:22

so how do they both change when you only type in one of them?

thedavidmeister15:04:44

that’s the bug

micha15:04:34

it seems impossible

micha15:04:42

if the two inputs do not share anything

micha15:04:51

then how can they affect the value of the other one?

micha15:04:59

they must be sharing value cells

l1sp3r15:04:13

had a question about dev flow, reagent / figwheel seems quite popular, is there an equivalent for Hoplon, or am i asking the wrong question?

micha15:04:45

you can use the live reload stuff with hoplon, too

micha15:04:56

it's a little different though because hoplon doesn't have the virtual dom

l1sp3r15:04:04

figwheel is packaged with leiningen isn't it?

micha15:04:10

boot has a similar thing

micha15:04:22

with boot it's separate composable tasks

micha15:04:28

instead of one thing

micha15:04:45

boot watch reload hoplon cljs

l1sp3r15:04:46

is there a batteries included tutorial somewhere?

micha15:04:56

yep you can use the hoplon template

micha15:04:04

it has it all set up by defulat

l1sp3r15:04:17

simple_smile seems like someones actually put some thought into this

thedavidmeister15:04:41

@micha: what do you mean by “sharing value cells”?

thedavidmeister15:04:44

:value (j/cell= (if-not (= :new id)
                              ((keyword key) entity)
                              "”))

thedavidmeister15:04:48

that’s in a loop-tpl

thedavidmeister15:04:13

with a new “entity” for each thing in the loop

thedavidmeister15:04:51

(let [entity (j/cell= (if (integer? id)
                            (into {} (d/touch (d/entity state/conn id)))))]

micha15:04:12

what about id?

thedavidmeister15:04:05

they have different id

thedavidmeister15:04:06

actually, you can see that both the “a” values are in the db under :min and :max

thedavidmeister15:04:09

they just don’t render

thedavidmeister15:04:54

#datascript/DB {:schema {:item/list-id {:db/valueType :db.type/ref}}, :datoms [[4 :max "a" 536870916] [4 :min "a" 536870915] [4 :item/list-id 3 536870915]]}

thedavidmeister15:04:00

the only thing i can think of

thedavidmeister15:04:15

is that because hoplon is re-using the input, instead of deleting and rebuilding it

thedavidmeister15:04:41

:value doesn’t “notice” that it’s a “different input"

thedavidmeister15:04:48

but i dunno, maybe that doesn’t make any sense

thedavidmeister16:04:42

it’s very weird that i can reliably reproduce the bug when the “before” and “after” inputs have the same string value

thedavidmeister16:04:48

but never if they have different values

thedavidmeister16:04:30

i can do it in the same list too >.<

thedavidmeister16:04:07

anyway, i have to crash

thedavidmeister16:04:28

i’ll see if i can make a smaller test case tomorrow or later in the week

raywillig19:04:47

hey @micha i’m using your awesome workflow machine for form processing. I have a workflow that’s broken up in to several screens on a modal popup. I’d want to do validation before moving on to the next screen. would you set that up as separate forms?

micha19:04:13

@raywillig: yeah you probably want the user to be able to reload the page for instance at any point in the process and not have to start over right?

micha19:04:45

so i'd probably organize them as if they're separate things, with their own validation, etc

raywillig22:04:30

@micha, when calling a defrpc from the repl, is rpc/pre skipped?

micha22:04:00

also if you call it from another rpc function

raywillig22:04:35

is there a way to force it? so that I can check validation?

micha22:04:58

sure you can just bind *request* to something

micha22:04:25

that's what it uses to know if the call is via http or not

micha22:04:46

(binding [*request* {}] (my-rpc-fn ...))

chromalchemy22:04:24

@micha Chris Granger seems to be in the "Spreadsheet as fundamental UI" camp with Eve project https://groups.google.com/forum/#!topic/eve-talk/3_zgustu0r0

raywillig22:04:01

@micha: got it to work, apparently needed to bind pre to something truthy

micha23:04:23

@chromalchemy: yeah totally, although i think their hiccup virtual dom thing makes it less intuitiev

chromalchemy23:04:56

Tables may be good for keyboard data entry, and regular data views. But I like to think in relational graphs, and arbitrary-formatted data with graphical semantics. Hoplon provides the means.... simple_smile