Skip to content

Conversation

rowanc1
Copy link
Member

@rowanc1 rowanc1 commented Apr 6, 2020

Progress

  • Have introduced two packages: store and basic
  • Packages are now in typescript 🎉
  • Packages are now linted (with eslint)!
  • Included testing for the store and evaluation scripts
  • Move to a redux architecture that can include the evaluate middleware or not. (by default this will be included)
  • Opinionated styles are starting to be removed (e.g. color, font)
  • single file for each component
  • Removed boiler-plate code for creating components, including fighting with typescript generics so new components don't have too!!!
  • Performance improvements - only render the components that actually changed! 🚀

There are a few suggested breaking changes on name/value/bind as well as transform on the display component. I would be curious on hearing from @lrq3000 or @J2thearo on these changes to see if they make sense!

New Basic Components

Working with material web components to bring new components!

  • <ink-switch> - material UI switch
  • <ink-checkbox> - material UI checkbox
  • <ink-radio> - material UI radio
  • <ink-select> - material UI select box New Component: Dropdown Selector #9
  • <ink-input> - material UI textarea
  • <ink-visible> - Div that makes things invisible on demand
  • var list? do we need this? Now that there is CSS control, maybe not?

Changes to Ink Article

  • Now using <article>
  • Now using <aside> for both asides and callouts. <aside class="callout info">
  • These are now just plain CSS - so should be easy for people to re-theme
  • <ink-code> and <ink-demo> have been moved over
  • <ink-equation> is ported, and works a lot better on the rendering side (no more 1-frame lag)
  • Outline (to make @choldgraf happy) iooxa/ink-article#2
  • Figure --> <figure> iooxa/ink-article#3
  • Helper functions to render math inline.
  • Quote iooxa/ink-article#4, byline iooxa/ink-article#5
  • Card --> not sure about this one?! iooxa/ink-article#7
  • Some level of cross-referencing on the page (is this <ink-ref>)? And a numbered attribute? iooxa/ink-article#6

Charting

I would like to get this merged and then can move on to the chart library -- it is a different animal and needs some love.

Resume

Similar to charting, this should likely be mostly CSS? Different library, out of scope of this PR.

User-defined functions

Runtime State

I have moved to a single redux store to manage the state. This is split up into {variables, components} the low-level interface looks like:

const x = store.dispatch(ink.actions.createVariable('scope.x', 3));
const max = store.dispatch(ink.actions.createVariable('scope.max', 9));

const range = store.dispatch(ink.actions.createComponent(
  'range', 'scope.myRange',
  { value: { func: 'x' }, min: { value: 1 }, max },
  { change: { func: '{x: value}' } },
));

// Can set/get the properties
x.get()
x.set(4)
const { min, max } = range.state;

A version of this will be exposed in the window as ink so that you can get/set variables in other programs which is needed for more control: (e.g. see #12 #10).

Creating a new component

I have added a base-class BaseComponent that does the generic registering and unregistering of the component as well as a class wrapper @withInk that injects properties that you define in a ComponentSpec and give the relevant setters/getters for properties and events as well as playing nice with lit-element. The basics of this new setup gets the boiler-plate from 74 --> 18 lines of code (~25% of the size). The @withInk wrapper I think is also more accessible (to debug etc.) than what I had before.

export const InkDisplaySpec = {
  name: 'display',
  description: 'Inline display of values',
  properties: {
    value: { type: types.PropTypes.number, default: NaN },
    format: { type: types.PropTypes.string, default: '.1f' },
  },
  events: {},
};

@withInk(InkDisplaySpec, { bind: { type: String, reflect: true } })
class InkDisplay extends BaseComponent<typeof InkDisplaySpec> {
  updated(updated: PropertyValues) { onBindChange(updated, this); }

  render() {
    const { value, format } = this.ink!.state;
    return html`${formatter(value, format)}`;
  }
}

Breaking Changes

Name/Value/Bind

There are a couple of issues with the previous naming conventions. It was is not specific enough for multiple event types (hover, drag, click, etc.) and there is no option for being able to add named components. This could be very helpful to react to events when a component enters the screen, show the component.min value, etc. The previous name attribute was used to set :value="${name}" and bind="{[name]: value}" which amounted to two way binding in the component. This was a handy shortcut and I think it should stay, however, be renamed to bind now that events are explicitly named.

  • name for components will now be used for the actual name of the component (should be unique in that scope)
  • introducing named events, which will allow components to have multiple types of events
  • bind is used as the default event type on components that allow it.

Previous version:

When you eat <ink-dynamic name="cookies" min="2" max="100"> cookies</ink-dynamic>,
you consume <ink-display :value="cookies * 50" format=".0f"/></ink-display> calories.

Changes to:

When you eat <ink-dynamic bind="cookies" min="2" max="100"> cookies</ink-dynamic>,
you consume <ink-display :value="cookies * 50" format=".0f"/></ink-display> calories.

This is equivalent to:

When you eat <ink-dynamic :value="cookies" :change="{cookies: value}" min="2" max="100"> cookies</ink-dynamic>,
you consume <ink-display :value="cookies * 50" format=".0f"/></ink-display> calories.

Action / Button

The bind syntax doesn't make sense for the ink-action and ink-button components, it should really be an event and there isn't a value to "bind" to. This should change to :click to return an event transform object.

Transform

  • I am not convinced that transform should remain on the display component. This can be done in a single call in the :value function: <ink-display :value="fullName.split(' ')[0]">. I do think that it should remain in the dynamic as the value is different than how you might display the variable.

Questions

  • Should the store package be called runtime? Would be similar to svelte, observable, idyll, etc.
  • Move the repo to @iooxa (likely) probably leave the repo ink-components on github/npm for the main package. So packages are @iooxa/runtime etc.
  • A lot of the ink-article work is really just CSS, these should probably be removed as WebComponents. e.g. ink-aside --> aside ink-article --> article as these are standard HTML components.
  • I am questioning things like ink-p and ink-a, these are really common attributes and when parsing (e.g. SEO) have semantic meaning. The visible attribute is the main use case for these so that they react to changes in ink and the <ink-a> might have to be different? There could be something that is added that looks at the dom and toggles these on/off based on the state?
    • I really wanted to change to inherited components <a is="ink-a">, but that is not supported by Safari. 😞 (and because of that lit-element doesn't support it either). Update: This could be done by wrapping the <a> in a custom component.
    • Explore the visible attribute on children of an article
    • Explore ink-preview
  • Should the setter on variable/component shortcuts return a promise? Right now it is sync, but that might not be the case if different evaluation middleware is used.
  • Add an example of a named component that is referenced.
  • Can you get access to the variable properties somehow? Sometimes nice for format so that you can define it once (right now this is set on bind if it is defined in the spec).

Outstanding Work

  • Adding in the other packages (CV, Chart, Article)
  • Packaging and publishing
  • Move the code I have left in /src into appropriate packages
  • Documentation update.

See #11

Copy link
Member Author

@rowanc1 rowanc1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Random comments!

import { Middleware } from './types';
import { DEFINE_COMPONENT_SPEC, DEFINE_COMPONENT } from './components/types';

const triggerEvaluateMiddleware: Middleware = (
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also be able to trigger evaluation manually.

@lrq3000
Copy link

lrq3000 commented Apr 7, 2020

That looks awesome!!! Thank you so much @rowanc1 for working on it, I'm convinced ink-components has an amazing potential, and in fact it's already working so well that it's already my de-facto choice to make explorable components. I am in fact experimenting with bridging it with Brython (as I am more of a pythonist than a javascript coder :-) ) and so far it's working amazingly well!

So I'm not a great js coder, I'm kind of stuck in the era before node.js, so I'm going to give my perspective as a user, not as a developer as this is over the top of my head :-)

For the runtime state and exposition of ink as a js object that can be used in custom javascript code, that sounds awesome! Idem for creating new component, I didn't need to create my own components for now, but I can see the great potential here!

The change of bind to :change or :click or events is I think very good, as bind confused me a lot, even though I know how it works now, I still have a hard time getting my head around whether I should use :value or bind. With :change it's much clearer that the event happens when the component is used. Idem for :click.

About the removal of transform in the display component, yes I also found it was redundant, but I could understand it being retained to have a standardized API across components, but if not all components have this property (ie, it's not standard) then it can be safely removed I think.

About ink-article, I don't know what this does because it's not documented, but as a user I do not care much if it's using JS or CSS as long as it works in the browser without a server ;-) If this is useful to make scalable documents, then I would like it to stay, but that's up to you, you know better what is more maintainable and in the scope of the project (or if you have plans to provide a similar function but in another fashion, then no problem).

About ink-p and ink-a, yes I agree it would be better to drop them in favor of a wrapping approach, because as you stated it's better for SEO, but also because it's more generic, then ink can be used as an enhancer of any html tag or even other custom web components! eg something like

<ink-visible name="linkhider"><a href="someurl"></ink-visible>sometext<ink-visible name="linkhider"></a></ink-visible>

Would be generic and simple enough to use, although a bit redundant. If it could be set as an attribute (eg: <a href="someurl" ink-type="visible" ink-name="linkhider">sometext</a>) it would be more concise but if this doesn't work on Safari as you say then the wrapping approach is fine I think.

About the rest, such as the store package renaming, it's over my head so I can't say anything ;-)

However as an almost naive user, I fear that the transition to typescript will require compilation the library with node, so could you confirm whether ink.js will be embeddable in any HTML page as it was before with a simple <script src=""> tag?

Thank you so much again for your amazing work! :D

@rowanc1
Copy link
Member Author

rowanc1 commented Apr 7, 2020

Thanks so much @lrq3000 for the feedback, really helpful.

  • I haven't run across Brython (or Transcript) before, they both look pretty promising for bridging the python/javascript worlds. If you have any examples, I would love to see them!
  • Glad to hear that you are up for changing the name/value/bind syntax - I think the outcome of this will be much more sensible - and confuse fewer people in the future!
  • The transform is only in the display because of class inheritance / me being lazy before. I think that it can be safely removed. Keeping it in the dynamic is important, it allows you to scrub/scroll over one number (value) and then transform that value into something else. e.g. arrays/emoji/text scrubbers:

emoji-slider


  • There are a lot of undocumented components in there that I was mostly using for my website (resume/CV elements, layout, cards, citations, figures, etc.). As I start to pull them out and clean them up I will document them this time ...!

  • The ink-a element will probably stay in some form, see below. This allows you to get some details about a link before you click on it. As you suggest, I think wrapping is better though and it remains a progressive enhancement rather than failing to become a link (if js is off or a scraper doesn't run a browser).


anchor


  • I think that the ink-p/div/span elements I will remove. I was looking into perhaps using the visible attribute and searching for these on the page. This would allow something like:
<ink-range bind="icare" min="1" max="5"></ink-range>
<span visible="icare > 3">
    This is visible if you really<span visible="icare > 4"> really</span> care!
</span>
  • I will continue to play around with this and see what works, I just discovered that you can't use selectors with a preceding :, which is important as you would need to find all of them efficiently, but it means the syntax is a bit inconsistent.

  • I was hoping to use the customized built in webcomponent which upgrades a known element through is="my-component" rather than starting from scratch through <my-component> (this gets around all the SEO as well as progressive enhancements, etc.). Apparently the Safari folks are opposed to this even though it is a W3C standard for the last few years. It was kinda an interesting thread to read to see how these standards evolve.

  • Typescript compiles down to javascript, and the usage of the library shouldn't change (single script import).

I will update this thread again soon as I make some progress. Thanks again for the feedback/usage!

rowanc1 added 3 commits April 7, 2020 15:27
* remove dictionary type
* Standardize the interface to shortcuts
* funcOnly --> has {func, value} to define properties
* InkVarSpec
* Variable can get accessed/set like a component
* InkAction, InkButton, InkSwitch, InkVisible
* Variables now act like components (using same code)
* Better formatting and variable parsing
@rowanc1
Copy link
Member Author

rowanc1 commented Apr 8, 2020

New components!
image

@lrq3000
Copy link

lrq3000 commented Apr 8, 2020

That looks awesome! The new widgets are very welcome additions! Sorry if I may sound over excited in all my posts here, but really the library was already awesome before, and now it's getting even better! Thank you so much for all your work on it!

Yes for sure if I succeed in using ink-components with Brython I will gladly provide you with a sample :-) Furthermore, Brython doesn't have native support for widgets (nor widgets provided by charting libraries such as Bokeh, although the Bokeh charts can be used), so that's why ink-components can be a great complement for data analysts who want to quickly make interactive charts!

Here are a few additional suggestions/comments:

  • yes transform is very useful, I agree it's nice to keep it :-) I understood you wanted to remove it in ink-display because it was useless there, and I agree, since it was not done for API consistency in the first place then there's no reason to keep it :-)
  • About ink-a : aha! That's how you made these nice previews! I thought you used another library such a JQuery. May I suggest to maybe extend ink-components to also include "ink-attributes", which would be special attributes that would be added to standard html tags to add some interactive behaviors? For example we could have a ink-preview attribute to make any tag with a href component display a preview on hover? Then this would work for a tags, but also for any other html (or even custom/web component tags) as long as there is a href?
  • similarly for ink-p/div/span, I would suggest a ink-visible attribute instead of visible just for consistency and to avoid potential mixups with other libraries or (possibly future) html standards. Otherwise the use case you outline, with the attribute value being the condition, would work very well I think!

@lrq3000
Copy link

lrq3000 commented Apr 8, 2020

Also as soon as you compile a javascript version of the new branch I will gladly test it out (since I don't know how to compile typescript) ;-)

- export lit-element and html out of basic
    - important for inheritance downstream
- moved provider to the runtime
- display not shows in the light-dom
- style fixes for slider
- visible element is now just toggling the hidden attribute
   - important for textContent (e.g. in ink-equation)
- fix build of TS for ink-basic for import down stream
@rowanc1
Copy link
Member Author

rowanc1 commented Apr 9, 2020

Just some other running thoughts:

The idea of binding to a variable should give you other things, like the format, description which is actually nice. (e.g. hover tooltips for explaining a variable?) I was trying to do a conditionally written math equation that updates --- and I could not write the format in the variable when also wanting to conditionally change things.

equation visibility

So I might be going back on the transform piece for displays. :) Anyways, small changes.

I took @lrq3000's advice and went with an ink-visible element. All it does is wrap and pop a hidden attribute on things. This actually works really well with the equation when reading from textContent vs innerText which respects visibility. Had to make a few changes in the display to show the text in the light dom rather than hiding in the shadows. I put a note to explore the visible attribute in the above as a question and will come back to it. I think it may have some advantages.

@lrq3000 I like ink-preview -- this has a lot of fun applications to things like citations, or previewing a named equation, etc. The link side of things has to pre-compute the image, so slightly less useful in plain-js world without a server doing some of the tasks for you.

I will try to get things published by mid-next week (hopefully sooner?!) to start playing with them. Thanks in advance for your help! (Also, the enthusiasm is always welcome ... 🚀)

@choldgraf
Copy link

heya - a quick question from me. I think we discussed before but I don't remember where it ended. I wonder if there are ways to bridge the widget-y components and their values with the ipywidgets ecosystem. It has similar visual elements that store some state at the javascript layer, and that state is originally generated from Python. Do you see any interaction points where that state could be shared by, say, an ink-js component?

What I am imagining here is something like "a person uses Python to generate a number, and produce a sliding widget that controls the number's value, and then uses an ink-js component to display that number in the text somewhere". Does that make sense?

@rowanc1
Copy link
Member Author

rowanc1 commented Apr 10, 2020

Hey @choldgraf, one of the main motivations behind this work was to make the components work with other environments. I have pulled out all of the state manipulation code into a runtime package, so I think it should now be (relatively) straight forward to connect it into the Jupyter ecosystem. I actually gave a scipy lightning talk about this sort of idea in (gosh) 2015: https://www.youtube.com/watch?v=xf3A6fA77XA

I am sure things have been updated a lot since then, but I think we could figure it out. :)

I think what likely has the most potential is to bring these components inside of the markdown cells (which can have raw HTML, and in future can perhaps be supported by myst directives), and then have some sort of bridge between jupyter and ink. This would basically bring the widgets layer into the prose. I think the tricky part would be naming the variables -- probably something like your glue code would work well there? I would love to talk this through some more in the next few weeks!

rowanc1 added 2 commits April 14, 2020 09:07
- Wrap state in "ink". Helps with merging with other states.
- Better show that evaluation is using eval (dangerous)
- Trigger evaluate on removal of components as well!
- Expose evaluate actions to other packages
- Better minimal styling on range slider
- Throttle the range slider events.
- Remove silly string formatting and put it in var.
- Dynamic now has "after" property which is set with innerText
  - Slot is now hidden
- No cleaning of dist folder.
- Export components from package
- equation has an editing property to show the slot.
  - Now works better with textContent
- dist folder is not deleted on rebuild
- Components exported, package name fixed
@choldgraf
Copy link

I have pulled out all of the state manipulation code into a runtime package, so I think it should now be (relatively) straight forward to connect it into the Jupyter ecosystem

🤩

I am sure things have been updated a lot since then

most definitely haha

I think what likely has the most potential is to bring these components inside of the markdown cells

Agreed - it would be great if you could, e.g., create a widget slider with ipywidgets, and have it connected to text in a markdown cell somehow (with a two-way connection, tangle-style). Wanna direct your attention to this issue: jupyterlab/jupyterlab#272 which I think might be a nice way for jupyter lab to support a more extensible flavor of markdown that packages could easily build on top of. Maybe you have thoughts there?

@rowanc1
Copy link
Member Author

rowanc1 commented Apr 15, 2020

I have created separate repositories for each of the packages, leaving this PR a bit of a shell. :)

I will continue to update this repository, I see it being a pulling together of relevant other packages (chart, cv, etc.) with decent default styles. And those pieces are not yet complete! As such I am going to leave this PR open, and reference the various other packages.

I have published everything to npm, @lrq3000 that means if you want to play with it that would be awesome. I reworked one of your examples here:
https://jsbin.com/puruhif/edit?html,output

Two packages that you can get:
@iooxa/ink-basic
@iooxa/ink-article
The ink-article one includes all the ink-basic stuff, so having both in the head will break things.

Some of my next tasks are to do a first pass on documentation and probably get the charting into a decent state.

@rowanc1
Copy link
Member Author

rowanc1 commented Apr 17, 2020

Initial chart refactor is complete here:
https://github.com/iooxa/ink-chart

I haven't deployed quite yet, there are some problems with the ink-chart-eqn element, with some rambling issues here: curvenote/runtime#12

The new updates made this refactor pretty solid, and cut back a ton of code.

@rowanc1 rowanc1 mentioned this pull request Apr 28, 2020
@rowanc1
Copy link
Member Author

rowanc1 commented Apr 28, 2020

I am closing this PR and will merge in the @iooxa/article PR #14. I will release these and write up some release notes in the next few hours.

I have put some work into a website at https://iooxa.dev. Still needs some love on the responsiveness, but getting there. :)

@rowanc1 rowanc1 closed this Apr 28, 2020
@rowanc1 rowanc1 deleted the v1 branch April 28, 2020 17:23
@choldgraf
Copy link

choldgraf commented Apr 28, 2020

It looks great!

FYI, may wanna set your scrollbar to auto!

image

@rowanc1
Copy link
Member Author

rowanc1 commented Apr 28, 2020

Fixed! :) curvenote/curvenote-dev@9eaed9f

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants