rdfjs / shacl-ui

SHACL-driven UIs
https://rdf.js.org/shacl-ui/
11 stars 2 forks source link

Scope: Forms vs Views #4

Open tpluscode opened 1 year ago

tpluscode commented 1 year ago

In addition to the #2 I think we should define if the interest of this task force is only forms or also "views" or pages.

In my experience, the two are quite separate and while DASH vocab comes with a set of terms for editors and viewers, I find that it is not as simple as annotation properties with both to produce good UI.

I found that a form is usually based off a SHACL Shape, describing a single resource or a set of closely related resources which form a cohesive whole. This is at least the case in a client/server, resource-oriented scenario where a client retrieves the shape and a single resource to edit and then saves it back. That said, an approach which sits closer ot the data itself is not so much different in that any modifications and shapes are closely modeling the graph model with very similar semantics to how one would model a OWL ontology, etc.

On the other hand, specialised views will likely span multiple resources and combine their descriptions in a different way, aiming to provide a different projection of the actual data. View Shapes would likely use deep property paths, partial queries, aggregations, and richer components to provide the user with an interface to fulfil a very different role than a "form turned read-only". Some examples of what I have in mind could include:


Thus in practice, I have been maintaining a separate shape for editing and a separate for displaying.

I would consider keeping such a separation in the spec, where we'd have one spec describing how to use SHACL to build editable UI, and another one dedicated to building "pages" with SHACL.

tpluscode commented 1 year ago

To elaborate a little on the last bullet above, I have recently been prototyping code to render entire pages from SHACL. The important feature for me was to use web components and server-side rendering. This forced me to slightly rethink how any given shape declares the UI components to use so that declarative HTML composition is possible. The outcome is a deviation from the DASH as I understand it, where I allowed RDF List objects of dash:viewer property.

For example, in my data I have a link of precomputed filter URLs to pages where the ?i query is an initial of the brand names.

<brands> a schema:WebPage; 
  hydra:view [
    a :AlphabeticalPager ;
    :page <brands?i=a> , <brands?i=b>, <brands?i=c> ; # ...
  ] ; 
.

<brands?i=a> rdfs:label "A" . # etc

I wish to display each link in a pager component. By separating the logic of displaying a link from the pager component itself, I arrive at this markup

<ex-pager>
  <!-- for every :page, render a link -->
  <a href="brands?i=a>A</a>
  <a href="brands?i=b>B</a>
  <a href="brands?i=c>C</a>
<ex-pager>

The problem was that I could not define this composition with the current toolkit (or so I think). Thus, the dash:viewer list solution which describes the above somewhat like

<BrandsPage>
  a sh:NodeShape ;
  sh:targetNode <brands> ;
  sh:property [
    sh:path hydra:view ;
    sh:class :AlphabeticalPager ;
    dash:viewer 
      ( 
        ex:Pager # dash:MultiViewer, render the <ex-pager> wrapper component
        dash:DetailsViewer # delegate the rendering of individual links to nested shape
      ) ;
    sh:node [
      sh:property [
        sh:path :page ;
        dash:viewer dash:LabelViewer ; # render <A> link
      ] ;
    ] ;
  ] ;
.
danielbeeke commented 1 year ago

I have used a similar concept to dash:Viewers but always used views of documents. I think using multiple documents in a composition view might become very complex.

tpluscode commented 1 year ago

Why do you want to use SHACL / RDF to define whole HTML pages?

A SHACL Shape which describes an entire page allows me to retrieve the exact data needed to populate it and then render. Both important for performance and Server-Side Rendering in some way.

Do I understand it correctly that you would be somewhat typing HTML in turtle / RDF?

Not exactly. Clearly, my focus is HTML pages and the viewers in the above example translate to composition of HTML elements but they are abstract and could be used to compose an interface in another technology which keeps a similar tree-like component structure.

danielbeeke commented 1 year ago

How much of this is already in SHACL? Isn't SHACL by default meant for a one to one relationship between a shape and a document? How do you fetch multiple documents with pagination for example?

Or is that exactly what you would like to add with SHACL UI?

tpluscode commented 1 year ago

Yes, some of what you bring up would ideally be part of SHACL-UI

Isn't SHACL by default meant for a one to one relationship between a shape and a document?

Yes, but I suppose you could dynamically load resources (documents?). The root shape may include hints for that, etc

HolgerKnublauch commented 1 year ago

I am quite interested in this topic. In TopBraid we render resource forms with a generic top-to-bottom list of property widgets, grouped into sh:PropertyGroups and sorted by sh:order. I believe it would be beneficial to be able to say, for example, that a Person's firstName and lastName should appear horizontally aligned instead of vertically. This suggests some sort of

layout structure. Likewise there could be annotations on whether the label of a property widget should appear above or to the left. And there could be "static" areas such as descriptive text or other media that are placed somewhere on a form. The don't need to be completely "static" but could be computed using SHACL-AF node expressions or similar.

I am aware this is open-ended and we don't want to reinvent HTML here. But a box layout model with static parts may go a long way. And it should be declarative so that non-HTML engines can also use that information.

@tpluscode do you have a draft vocabulary for such things that you would want to share as starting point?

tpluscode commented 1 year ago

I believe it would be beneficial to be able to say, for example, that a Person's firstName and lastName should appear horizontally aligned instead of vertically. This suggests some sort of layout structure

I agree. Good form generators out there do have some control over the layout which is more sophisticated than grouping and ordering.

What I'm trying to say is that forms and views are likely to be represented differently in SHACL Shapes. I also prefer completely separate implementations because forms and views have a very different model for the internal state of what's being rendered.

@tpluscode do you have a draft vocabulary for such things that you would want to share as starting point?

In fact I don't have much. For forms, I too have been satisfied with sh:group/sh:order. For views, at some point I stitched together an imperfect solution where I would property shapes or groups with CSS selectors to force specific HTML structure, styles and attributes.

In more recent implementation I rely simply on element composition and both layout and styling fall out of scope of the shape itself. Similar to the example above, a top-level dash:viewer would be set to a "layout component". The only addition is html:slot (I previously used schema:identifier) to define where the child properties are going to be rendered.

<> a sh:NodeShape ;
  dash:viewer ex:SidebarLayout ;
  sh:property [ sh:path schema:title ; sh:group <#header> ] ;
  sh:property [ 
    sh:path skos:related ; 
    sh:group <#sidebar> ; 
    dash:viewer ( ex:LinkList dash:LabelViewer ) ;
  ] ;
  sh:property [ 
    sh:path schema:articleBody ; 
    dash:viewer dash:HTMLViewer ;
  ] ;
.

<#header> html:slot "header" .
<#sidebar> html:slot "sidebar" .

The final result could be similar to

<ex-sidebar-layout>
  <span slot="header">
    <!-- render component for schema:title -->
  </span>
  <!-- 
    Notice again the rdf:List used for dash:viewer, 
    where the first is a dash:MultiViewer to act as a container 
  -->
  <ex-link-list slot="sidebar">
    <a href></a>
    <a href></a>
    <a href></a>
  </ex-link-list>

  <!-- render component for schema:articleBody -->
</ex-sidebar-layout>

As I said, there is no need for explicit layout or styling hints. Layout is defined in the implementation of <ex-sidebar-layout> where HTML slots handle placing child properties in the desired places. The nice part is that the exact same mechanism works on any nesting level.

danielbeeke commented 1 year ago

I like the usage of rdf:List for dash:viewer I also think these slots are very nice. I think it is a clean separation.

It made me think.. do we want layouts that are portable?

If we want them to be portable and be defined in rdf we could use something like css grid and grid regions. The regions would be somewhat similar to slots. The main difference with slots and separation is that some css grid rules should be defined for the layout.

I think we could maybe grab a subset of the css grid rules that would be easy to use in native code.

I am not sure if portability of layouts would be a must. I do see one use case.. a system with federation and open data types, such as mastodon. One app could define a media type (a recipe for example) and also give a reference for how to render those recipes.

In that case simple layouts such as 'two column', or 'left sidebar' could be interesting.

tpluscode commented 1 year ago

It made me think.. do we want layouts that are portable?

Very good point! Contrary to my remark on the last call, I am most certainly JS-only focused. That said, there is nothing other than the slot concept which is browser-specific but I surely do not consider other targets in my work.

we could use something like css grid and grid regions

CSS grid is a good suggestion. It has wide support in browsers and should be easily adoptible on other platforms too. Alternatively, we could devise a abstract grid addressing and have CSS grid an implementation detail.

While I still keep the mental separations between views and forms, the CSS grid would very much work for forms, such as in the "placing first and last name next to each other" scenario above. On second thought, a similar mechanism could also work for views, where an individual group is laid out as a grid too. I think this is slightly unrelated to the "two column"/"left sidebar" which defines a layout on a different level

Now, one potentially problem with css grids is responsiveness. If the solution was to arbitrarily assign rendered viewers and editors to specific cells in a grid, the layout would be very rigid. Compare with the approach shown in this tutorial, where the HTML elements have meaningful class and are positioned using a grid indirectly.

In that case simple layouts such as 'two column', or 'left sidebar' could be interesting.

To complement the fine-grained layouting solution (css grid, or otherwise) we could still keep these abstract layouts and a new concept. A hypothetical dash:SidebarLayout is just as portable as dash:SingleLineEditor in that any platform can provide its specific implementation while keeping the shape agnostic.