Skip to content

unpoly/unpoly-backlog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

67 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Priority

Perspective

  • Callback to run before any render pass (see davidsums popup discussion)

    • up:render:prepare or up:render:setup or up:render:options
    • Would we rename up:fragment events to get pressure off the up:fragment namespace?
      • up:render:load
      • up:render:loaded
      • up:render:keep :(
      • up:render:offline
      • up:render:inserted :(
      • up:render:hungry :(
      • This would leave up:fragment:poll
  • Consider a callback to manipulate render options before interactive element

    • Either element-specific ([up-on-follow], [up-on-submit], etc.)
    • Or generic ([up-on-use="..."], [up-setup="..."], [up-before], [up-guard], [up-on-run] [up-on-activate="..."], [up-on-start])
      • Use is short, but conflicts with [up-use-data] and does not work for polling
  • Open overlays from template selectors

  • Support X-Up-Open-Layer: { type, ...VISUAL_OPTIONS }

    • Works for both success and failed response. There is no X-Up-Fail-Layer option.
    • Ensure that X-Up-Target also works for both success and failed response. There is no X-Up-Fail-Target header in the response.
    • Support unpoly-rails (up.layer.open())
    • Recommend to also set X-Up-Target
  • FM required overriding of history from the server

    • They really wanted { navigate }, no?
      • But can we really change navigation options at that point?
  • Remove params parsing for up.watch(), support formdata, ElementInternals

    • We can just parse the entire form with new FormData(), then filter on contained elements
      • We will still need to know form fields for that
    • Replace up.Params.fromForm() with new this(new FormData(form))
  • Consider [up-zone] as an origin-aware lookup without further logic

    • Also support :zone as a new pseudo
      • Layer priority beats zone priority
    • config.zoneSelectors = ['[up-zone]', ':main]
    • config.noZoneSelectors = ['[up-zone=false]']
    • I think without an { origin } we only want to check out :main. Maybe it's not configurable?
    • Do we want to allow [up-zone="name"] (and include it in targetDerivers), or should it be [up-zone][id="foo"]?
      • If we want to get rid of [up-href] (to enable morphing), we should encourage the use of IDs
    • If we want to mount arbitrary pages to mount in an [up-zone] it should be a parent.
      • e.g. content
        • But then we would have duplicate
          • section[up-main=zone]?
  • Maybe all guardEvents should have a loadPage() method

    • So it's more discoverable then event.loadPage()
  • Consider publishing { dataMap, placeholderMap, previewMap } as @experimental

    • Check if we have tests here
    • Check whether a single { map } option (or an object target) would be superior
      • up.render({ target: { '.foo': { data: { key: 123 }, placeholder: 'spinner' }, '.bar': {} }, url: '/path' })
      • up.render({ target: { '.foo, .bar' }, dataMap: { '.foo'. { key: 123 } }, placeholderMap: { '.foo': 'spinner' }, url: '/path' })
      • up.render({ target: { '.foo, .bar' }, map: { '.foo'. { data: { key: 123 }, placeholder: '.spinner' } }, url: '/path' }) => Doing something to the primary is a very common use case and great now
  • Consider publishing up.script.clean()

    • Would we need a fallback for { layer }?
    • Supporting the use case "recompile later" is hard because then we would need to update the idempotent-compiler-fn-tracking
  • Consider removing { content } option of createFromSelector(), affix(), etc. in favor of a third argument for constructing nested things

    • When we make this change we should also fix the inconsistency that a string arg is considered HTML, but Array is considered text (and will be escaped)
  • Allow to set custom close animation from link

    • Also support (or document?) up.layer.open({ closeAnimation })
  • Consider contracting more attributes into shorthand values

    • E.g. [up-emit] and [up-emit-props] should be just one thing
    • E.g. [up-scroll] and [up-scroll-behavior] should just be one thing (but what about all the reveal attributes? reveal-snap/top/pading/max)
    • E.g. [up-transition] and [up-easing] and [up-duration] should just be one thing
  • Evtl. sollte [up-back] den letzten History-Eintrag im Root (?)-Layer nehmen?

    • Back vs. Modals allgemein betrachten
  • In the layer config, openAnimation and closeAnimation is a function, but it receives no useful args

    • Give it { layer, value }
    • Document it
  • Consider up.RenderResult#primary or up.RenderResult#fallback

    • Updating ":main" via { fallback: true } must yield #primary === true
    • So maybe #primary, #expected, #intended or similiar is a better name than #fallback
  • Rename "loading partial" to "filling partial" or similiar

    • up:partial:fill
    • up.partial.fill()
    • So "load" remains the term for sending a request
    • So we can offer [up-on-fill]
  • Consider X-Up-Redirect so we can get rid of param-transporting in unpoly-rails

    • Unpoly would track all X-Up params of request and response

Next release

@stable

Previews

  • Do we need to publish parseNodesFromHTML() so people can build their own template handler?

    • Yes, we mention in the docs
    • Should probably be createNodesFromHTML()
  • Set layer.current to the base layer when applying and reverting previews

Demo

  • The demo is broken
  • Make sure form group validation still works after Bootstrap 5 Upgrade
  • Set tour bubbles for the new functionality

Cards

  • Make a skeleton for cards

Guide

  • Skeleton for menu
  • Skeleton for main area

Docs & CHANGELOG

  • CHANGELOG

    • up.Request#ended
    • Native :has() is required and the app will not boot without
    • Explain that the demo shows new optimistic rendering, check "Extra latency"
    • badResponseTime => lateDelay
      • config.lateDelay
      • { lateDelay }
      • [up-late-time]
    • Go through entire Git log
    • Explain that we are now smarter at splitting complex selectors at
    • Explain that tokens now default to comma-separation
      • Comma is canonical
      • Space-separation still possible in most cases
      • or-separation is deprecated
      • Also make sure to update doc pages!
      • This affects
        • [docs updated] [up-show-for]
        • [docs updated] [up-hide-for]
        • [docs updated] [up-scroll]
        • [docs updated] [up-focus]
        • [docs updated] [up-layer] or { layer }
        • [docs updated] up.on()
        • [docs updated] URLPatterns
          • [up-alias]
          • [up-dismissable], { dismissable }
          • [up-accept-location]
          • [up-dismiss-location]
          • up.cache.expire()
          • X-Up-Expire-Cache
          • [up-expire-cache] { expireCache }
          • up.cache.evict()
          • X-Up-Evict-Cache
          • [up-evict-cache] { evictCache }
  • X-Up-Target should mention (in the first paragraph) that it is both request and response header

  • Document [up-placeholder] and [up-preview] in up.status (main), up-follow and up.render()

  • Docs for up.Preview class and its methods

    • up.Preview#insert (and up.fragment.insertTemp()) must mention that:
      • The element is compiled and destroyed
      • An attached element is moved back to its original position (and not compiled or destroyed)
      • You can refer to a template
  • "Network issues" should talk about previews and placeholders under "Slow server responses"

    • Right now they only talk about feedback classes
  • Change with disabling

    • We now support { disable: Element }
    • We now support { disable: Array<Element|string> }
    • Disabling forms from links
      • Document that [up-disable] is now also available for [up-follow]
      • Document that options.disable is now supported for up.follow()
      • Add a section to /disabling-forms
  • Update render lifecycle

    • Callback table
  • Doc page for

    • Compile with data
    • Inline data
    • Manipulate in onRendered
    • Templating engine
  • Doc page /loading-state "Showing loading state"

    • Show how the preview can manipulate the DOM

      • Prefer to be additive
    • Show how the preview can inspect the context

    • Examples

      • Example: Placeholder
      • Example: Add a TODO
    • Previews are not applied when we have cached content to render

      • Also not shown when cache-then-revalidate
    • Multiple preview effects

      • Space-separated
      • Array
    • Reverting effects

      • undo()
      • returning destructor
      • Important: don't revert effects that you didn't cause
        • Check if you need to do something
        • All up.Preview methods do this
    • Placeholders

      • As embedded HTML
      • As template cloning
      • As function call in a preview
      • Placeholder for OpenLayer change will open a new temporary layer
        • This will be reverted in case the server renders unexpected content
      • Dynamic placeholders with embedded data object
    • Dynamic previews/placeholders

      • Placeholders
        • #template as .modifier syntax
        • Explain that you can compile template clones
        • Explain that you can always use [up-preview] or [up-preview-fn].
      • Previews can take arguments
      • In JavaScript you can use { preview: Function } or { placeholder: Function }
    • Built-in previews

      • Just a short list with links to existing docs
        • Disabling forms while working

          • Link to disabling
        • Marking active elements with classes

          • Link to .up-active
          • Link to .up-loading
        • Placeholders

        • Progress bar for late responses

          • Link to progress bar
    • While watching

      • [up-watch-disable]
      • [up-watch-preview]
    • Delaying previews

      up.preview('foo', (preview) => {
        up.util.timer(1000, function() {
          if (!preview.ended) {
            preview.run('other')      
          }
        })
      }) 
      
    • Optimistic rendering

      • Point to demo example & code
      • Signaling severe network problems (up:fragment:offline)
    • Showing loading state

  • Rename /loading-indicators to /progress-bar ?

    • Extract anything that is not about the progress bar
  • Rework the section "Styling active elements" in /up-active.

    • It should link to the new status guides
  • Rename up.feedback to up.status

    • Title "Status effects"
    • Package intro should summarize our fancy new doc pages
  • Doc page /navigation-bars

    • Redirect /nav-bars /nav-bar /navbars /navbar /nav
  • Document new render options

    • Do this after we have the new doc pages
    • up.render()
      • Document { preview: string|Function|Array<string|Function> }
      • Document { revalidatePreview }
      • Document { placeholder: string|Function|Element }
        • It also opens a new layer
    • [up-follow]
      • Document [up-preview]
      • Document [up-revalidate-preview]
      • Document [up-preview-fn]
      • Document [up-placeholder]
        • It also opens a new layer
    • HTML options ({ content, fragment, placeholder })
      • also allow a template
      • Also accept a function (is this tested?)
      • Accept a List
  • New watch/validate options

    • Document [up-watch-preview] and [up-watch-placeholder] wherever we also document [up-watch-disable] or [up-watch-feedback]
      • [up-watch]
      • up.watch()
      • [up-validate]
      • up.validate()
      • [up-autosubmit]
      • up.autosubmit()
    • Extend /watch-options with [up-watch-preview] and [up-watch-placeholder]
    • Watcher callbacks get { disable, preview, feedback, placeholder } options to pass on to rendering
      • With up.watch()
      • With [up-watch]
  • /opening-overlays and [up-layer=new] should say how to open an overlay from local content

    • String
    • Extract segments from /providing-html
    • [up-defer] supports [up-preview], [up-placeholder]

      • The docs already talk about fallback state. We could destinguish "while it's loading".
    • [up-poll] supports [up-preview], [up-placeholder]

    • Consider an authentication modal in https://unpoly.com/up:fragment:loaded#changing-render-options

    • Check if the unpoly.com homepage needs to mention status effects and optimistic rendering

      • Instantly respond with loading state, render optimistically, handle offline
    • Checken dass @see auf den Module-Pages die wichtigsten Sachen enthält

    • up:request:loaded should say it's low level and consider up:fragment:loaded instead

    • Release

      • Merge, push and deploy the new demo

      Backlog

      • Exposing the temp functions would make it easier to compose some effects without accepting an (preview) arg

        • Expose up.form.disable() (since we also expose it via Preview#disable()) => I'm not sure how useful this is in practice. Not needed in the demo.
      • Can we find a way to implement the new-task action as a server-rendered template? template = preview.cloneTemplate('#foo', { '{{text}}': params.text })

        clone = preview.cloneTemplate('#foo', { '.task-item--text': params.text }) clone = preview.cloneTemplate('#index', { 'tr': rows => rows.slice(20).forEach((row) => row.remove() })

        clone = preview.cloneTemplate('#foo') clone.querySelector('.task-item--text').innerText = params.text

        clone = preview.cloneTemplate('#index') [...clone.querySelectorAll('tr)].slice(max).forEach((row) => row.remove())

        // 169 bytes function cloneTemplate(template, processors = {}) { let result = template.content.cloneNode(true) for (let [selector, processor] of Object.entries(selectorMap)) { let matches = u.toArray(result.querySelectorAll(selector)) if (u.isFunction(processor)) { processor(matches) } else { matches[0].innerHTML = processor } } }

      • Consider publishing booleanOrString or booleanOrNumber

      • Consider waiting for custom elements to be defined

        • For focus
        • For scrolling
        • For watching
      • Support render lifecycle attributes for [up-defer] and [up-poll]

        • [up-on-loaded]
        • [up-on-rendered]
        • [up-on-finished]
        • [up-on-offline]
        • [up-on-error]
        • Right now this can be set by listening to a guardEvent and manipulating event.renderOptions
      • We often see multiple preloading entries in the log (mouseover, mousedown). Can we not log if we're already preloading?

      • Support array fields with watch()?

        • up.form.config.arrayFields = 'suffix' | 'all'
      • Does it matter that guardEvents do not set up.layer.current?

        • It should mostly be the right layer anyway, except for polling in the background or something
      • [up-validate] from a different URL

      • An a[href][up-defer] should be unclickable?

        • Remove tabindex
        • Remove pointer cursor
      • Remove isNull() and isUndefined()

        • The test is trivial, but it may be nice to not have to think aobut it
      • Allow to keep elements without remembering to mark elements as [up-keep]

        • config.keepSelectors
        • Allow { useKeep: '[up-keep], .search-input' } to keep additional elements
        • Elements can still opt out with [up-keep=false]
      • Docs: [up-transition] should document params [up-duration], [up-easing]. Same with [up-animation]?

      • Docs: up.morph() should document options.duration, options.easing. Same with up.animate()?

      • Allow up.reload() to reload multiple, potentially optional elements

        • Starting in 3.8 we could also use 3 times up.reload() and make a single request
      • Consider publishing { defaultMaybe: true } with a better name

      • Docs: Extract new doc page "Preserving state" from [up-keep] and /data#preserving-data-through-reloads

      • Docs: Installation methods may be unmaintained; Go through methods and focus on installing frontend assets

      • Docs: URLs like /up-transition should redirect to /a-up-transition

      • Should up.layer.open({ data }) set the data on the root element, not the layer element?

        • Users can still "compile" the layer element by observing up:layer:open
      • Docs: Extract /polling page from [up-poll]

      • Have a better error when Unpoly is loaded twice

        • We cannot do this when booting
        • Throw in namespace.js
      • Add a config to disable validate merging

      • Touch events can scroll the background of a drawer overlay on https://unpoly.com/

      • In a multi-step render pass, let compilers see all updated fragments

        • This would require us to delay compilation until all fragments are inserted
      • Consider whether Request#target, Request#context etc. should be setters that auto-set the corresponding header.

        • Would save code in mergeIfUnsent()
        • Would save code in setAutoHeaders()
        • We could also make #headers or #header() hallucinate new headers
          • Maybe hard since we allow write access headers[key] = value
            • Would be easier with header() and setHeader()
            • Still need to iterate over the whole thing for passing over headers to xhr
      • Consider an up:fragment:render event to modify renderOptions for all kind of passes

        • We have so many guardEvents now
        • Test that we can still modify event.renderOptions
        • Point against that name: We already have { onRendered } and that fires after DOM mutation, not before the request
        • Possibly offer originalEvent or something?
          • No! We would need to check their renderOptions for mutation. Who is interested in other events can use them.
        • Update docs: Render Flowchart
        • Update docs: Manipulate render options
      • { dismissLabel } should be able to contain HTML

        • Maybe rename to { dismissButtonContent } or something?
      • Allow variations of official layer modes

      • lightbox Layer mode

        • Where are we going to put the margin around the box, if we're not having a viewport?
      • Move custom spec helpers out of jasmine.foo() to specs.foo()

      • Unpoly Rails needs to ignore pseudo-elements

      • I think we can replace up.Rect.fromElement() with just element.getBoundingClientRect()

      • I think revalidation now loses :maybe marks. We should have more tests.

        • Case 1 => I think this is implemented
          • We're loading ".foo, .bar:maybe"
          • Server only renders ".foo"
          • Revalidation should just be for ".foo", or for ".foo, .bar:maybe"
          • It should be OK if revalidation response only contains ".foo"
        • Case 3 => I could live without this. => Or should we allow reavalidation with defaultMaybe: true ?
          • We're loading ".foo, .bar:maybe"
          • Server renders ".foo, .bar"
          • Revalidation should just be for ".foo", or for ".foo, .bar:maybe"
          • It should be OK if revalidation response only contains ".foo"
        • Case 2 => I think this is implemented
          • We're loading ".foo, .bar:maybe"
          • Server renders ".foo, .bar"
          • While we're loading .bar was removed from the page
          • Revalidation should just be for ".foo", or for ".foo, .bar:maybe"
          • It should be OK if revalidation response only contains ".foo"
      • Preserve text selection ranges when reloading / polling / revalidating

      • Should [up-poll] and up.reload() render failed responses if they match the target?

        • At least for reloading the element may actually have entered the DOM from a failed response
      • Site: Scrollbar styling https://makandracards.com/makandra/617528-chrome-121+-no-longer-supports-non-standard-scrollbar-styling

      • Per-link animation options for new layers

        • Support mutatable animation options for up:layer:dismiss, up:layer:accept
        • Parse [up-close-animation] for links that open a new layer
        • Parse [up-open-animation] as an alias for [up-animation]
      • The log message "Could not match primary target" should not appear when we're only working with fallback targets, and we happen to use the second one. E.g. first one is [up-main=modal], second one is [up-main].

      • Print a warning when we can find no better form group than the

        itself

      • Run macros (but not compilers) before history changes

        • This is super hard due to the way UpdateLayer and OpenLayer works
        • In particular we also give the guarantee that the current layer renders before other (hungry) layers
      • Give layers an [index] attribute so users don't style based on [nesting]

        • Do we give root an [index] as well?
          • We cannot use [index] here
          • [up-layer-index] would maybe a good default
            • Check vs. V4 plans
      • Docs should show DOM structure of all layer modes

      • Simplify animation API implementation with Element.animate()

        • Animation finishing could be implemented with Element#getAnimations()
        • This should also fix a regression for: Animations that fly in an element from the screen edge (move-from-top, move-from-left, etc.) no longer leave a transform style on the animated element. By @triskweline.
      • Don't allow a request payload for DELETE requests

        • Maybe make this configuratable as opinions vary here.
          • e.g. up.network.config.payloadMethods
        • Is this really important?
      • Consider parsing scroll-margin-top, scroll-margin-bottom for revealing

      • Allow [up-emit] for buttons

      • Docs: Explain how to convert JSON responses to HTML responses (discussion #562)

      • Convert .sass files to .scss

      • While morphing or destroying, disable pointer events on old element

      • Support { onLoaded }, [up-on-loaded] for polling fragments

        • Test: Can be used to skip() a polling response, polling then continues
        • Test: Can be used to preventDefault() a polling response, polling then continues
      • [up-disable] should disable links (discussion #561)

        • Within the form
        • As a stand-alone link
        • How to mark disabled links? .up-disabled ? Just [disabled]?
        • CSS: { cursor: not-allowed }
        • Also [aria-disabled=true]
        • Prevent with JS? pointer-events?
          • Also prevent keyboard focus with temporary [tabindex=-1]
        • Bootstrap already has a.disabled and users would like to take over that styling
      • Docs: Copy important up.render() options to up.layer.open() and a[up-layer=new]

        • In particular { content, fragment, url, document }
      • Look within :origin first

        • Remove similiar logic in up.FragmentProcessor: findSelector()
      • Remove [up-validate] from individual fields https://unpoly.com/up-validate#validating-multiple-fields

      • Support nonce rewriting for arbitrary attributes, e.g. up.safe_callback('124321321', '432423423')

        • But then we would need to know all the attributes we're rewriting to query them quickly
          • Or find them with XPath
        • OR we could support [up-on] with an event name and a nonce
      • Swapped body looks funny in Safari (has .up-destroying class and is grayed out with old content in inspector)

      • Allow to disable batching in validations

      • inlineStyle, setInlineStyle should work with custom properties

      • Jasmine matchers no longer get a customEqualityTesters argument

      • up.ResponseDoc: Only re-discover the origin when match is 'region'

      • Async compiler functions

        • See separate doc
        • Don't need to delay this until V4 as destructors no longer throw
      • Is the [up-keep=false] pattern good to keep?

        • E.g. for flashes: We could interrupt a keep chain this way, but then the element can never again be keepable => It's the same for polling
      • consider having dev builds with debug terser

      • Docs: /accessibility page

        • But where does it go?
        • Top-level?
      • 2024: Replace up.error.emitGlobal() with window.reportError()

      • Publish up.util.sprintf()

        • But rename it to something else, as sprintf() is something else
      • Docs: Custom input elements

        • Have your custom element expose { name } and { value }` properties (AFAIK Shoelance components already do)
        • Also { disable }
        • Make Unpoly aware of custom elements by pushing a selector to up.form.config.fieldSelectors.
        • Make Unpoly aware of custom submitt buttons by pushing a selector to up.form.config.submitButtonSelectors.
        • Limitation: One value per field. Checks for groups (.checked and .selected) are still hard-coded
      • Test that system compilers always run before user compilers => We already have macros if compilers need to use others?

      • Concepts [up-feedback] and [up-background] are overlapping

        • Should feedback: false imply background: true ?
        • Should we generalize the feedback option?
          • { feedback: ['classes', 'progress', 'custom'] }
            • But I always want classes?
      • Message when unknown target: Revalidating cached response for target "undefined"

      • Extend [up-switch] with [up-disable-for="..."] [up-enable-for="..."]

        • This is really confusing with [up-disable]
        • Or should we have an [up-switch-for] and [up-switch-effect]?
      • up:feedback:start / up:feedback:stop

        • On the layer, not an individual element
        • Needs to have reference to origin and targetElements
          • Must be the same element in :start / :stop, even if the DOM has changed in between => With .up-validating we have different points where feedback starts and ends.
          • This class does not exist. It was an idea detailed further down. => Or we could have a single up:feedback event with a promise for the duration
      • Do we want a preventable up:request:reuse event?

        • Maybe even :reused since there is no :loaded for cached requests
      • Experiment with property mangling vs. public API

        • Possibly do a unpoly.experimental-min.js ?
        • If we could test a minified version we could publish this
          • Do we really think about this when publishing?
          • This would need to be part of the regular build
      • Issue with splitting Immediate-Child-Selectors: https://makandra.slack.com/archives/C02KGPZDE/p1659097196752189

      • Can we get rid of the afterMeasure() callback?

      • Support { scroll: 'keep' }

        • Similiar to { focus: 'keep' }
        • Store scrollTops around a viewport in UpdateLayer
        • Possibly refactor tops to up.ScrollTopsCapsule
      • Check that the target (and all non-successful target attempts) are visible in the log

        • Possibly highlight
      • Do we want to support or in targets?

        • Is there any case other than [up-target], where we already have [up-fallback] ?
      • Should animations force painting?

        • I don't understand how the transitions work when we set both the first and final frame in the same JavaScript task
      • Maybe auto-submit should not navigate by default

        • How to restore this?
        • up-navigate ?
        • up-watch-navigate ?
        • up-autosubmit-navigate ?
        • Is it cool that search results would no longer get a URL update?
      • Does [up-back] use the previous layer location?

      • There was a case where { origin } should look inside an origin: up.render('.day_menu_dish_autocomplete .day_menu_dish_autocomplete--suggestions', { origin: titleInput, url: '/day_menus/suggestions' })

      • Expose up.fragment.config.renderOptions

        • Now that we have some opinion about the defaults { abort: 'target', focus: 'keep', revalidate: 'auto' }, users may want to deactivate
      • Try to have a 'hash' strategy in overlays

        • Also update doc page /focus
      • Split JavaScript => Do this after asset reconciliation

      • input.setCustomValidity() should block submission

      • Replace symlinks in dist/ with copied files

        • Not trivial, see hk/copy-dist-artifacts
      • Maybe a way to mark elements as keep before rendering?

        • This would also be up:fragment:loaded
        • Maybe a render option { useKeep: [....] }
          • With additional fragments to keep
          • Could be set both in initial render and in up:fragment:loaded
          • Would not require an [up-keep] on the other side
      • Consider keepSelectors and noKeepSelectors

        • Now that [up-keep] no longer has a value?
          • Note that we do have [up-keep] hardcoded in multiple places
      • Should we offer a way to manually add etags?

        • E.g. up.on('up:request:loaded', ({ response }) => { response.etag ||= sha1(response.body) })
        • Should this even be a default? => People can already set response.headers['ETag'], although that's not documented
      • Matching in the region of ancestors should use subtree, not just descendants of the ancestor

        • In a form we should always be able to say form:has(:origin) .descendant, even if the origin turns out to be the form itself
          • But :has() does not work like that?
      • [up-on-accepted-reload], { onAcceptedReload }

      • [up-on-accepted-validate], { onAcceptedValidate } (aber hier brauche ich immer einen param)

      • Consider removing all tagnames from public selectors

        • We already had to remove tag names of multiple-use attrs like [up-watch]
        • current
          • a[up-follow]
          • a[up-instant]
          • a[up-preload]
          • form[up-submit]
          • a[up-accept]
          • a[up-dismiss]
          • a[up-layer=new]
          • a[up-transition] __
          • form[up-transition] /
          • a[up-alias]
          • a.up-current
          • a[up-emit]
          • a[up-back]
        • deprecated
          • a[up-close]
          • a[up-drawer]
          • a[up-modal]
          • a[up-popup]
      • Can layer objects nullify their { element, tether } once closed? => This may cause async JS to throw, if that JS wants to go through the layer shortly after closing => How about we do this 10 seconds after { onFinished } or so?

      • It may be nice if guardEvents see the change's getPreflightProps().fragments

        • Either lazy with Object.definePropery() or, if we find that we're going to call getPreflightProps() anyway, just call it.
        • Can we memoize getPreflightProps() in FromURL ?
      • up.validate() always submits to the first submit button => Is this a bug or a feature that we use the same behavior as submit would?

      • When a layer reaches a close condition, should we still render [up-hungry][up-if-layer=any] elements in another layer?

        • Would be practical for flashes
        • Would be weird for API. Render events flying afterwards, we cannot communicate a RenderResult.
      • When opening a new layer, we should update [up-hungry][up-if-layer=any] elements => This is not trivial as OpenLayer cannot work with multiple steps yet => Maybe we can do a "secret UpdateLayer pass" once the layer has opened.

        • We would need to merge RenderResult objects
      • Custom attributes are not copied into [up-expand]

        • up.link.config.expandableAttributes ?
          • can also include regexp
            • maybe reuse matcher logic for badTargetClasses
          • what if you need to copy an individual class, not an attribute?
            • up:link:expanded event for custom handling?
      • up.link.config.expandableAttribute (attribute, element)

        • The element will always be a link
      • Support custom expire times via Cache-Control: stale-while-revalidate

        • Print a warning when we're discarding any other Cache-Control header format
      • Test and document [up-href]

      • Polling should automatically restart when the tab is re-focused

      • A validating fragment should get a .up-validating class

        • Group under up.feedback
        • Maybe we can have a private API
          • { feedbackLoadingClass: 'up-validating', feedbackActiveClass: false } ?
          • { feedback: { loading: 'up-validating', active: false } } ?
          • With history we have separate options { title, location }
      • Consider renaming up.fragment.config.navigateOptions to just up.fragment.config.navigate

        • There is precedence in up.layer.config.overlay
      • Firefox: Focus loss after disable is not always detectable synchronously

        • Force Repaint does not help
        • setTimeout() helps (wait for next render?)
        • We fixed this in up.form.disableWhile(), but it may still be broken for up.fragment.config.autoFocus options suffixed with "-if-lost"
      • Consider whether validation requests should be background requests by default

        • We should also have [up-watch-background]
      • Why does [up-validate=form] target the first form for some users?

      Documentation

      • Should we offer a { placement: 'merge' } to merge children?

        • By selector?
        • How would that relate to recursive merging?
        • We don't need it for flashes, [up-on-keep] suffices here
          • [up-on-keep] can already support merging, morphing, etc.
            • However it's impractical to use it for regular fragment updates, you'd want .target:merge or something.
      • Example for a custom overlay

      • Activating custom JavaScript

        • (Currently sitting in up.compiler())
      • Rendering from strings

        • { document }
        • { fragment }
        • { content }
      • Preloading

        • (Currently sitting in a[up-preload])
        • On Hover
        • Programmatically
          • Example: Preload next/prev
      • Polling

        • (Currently sitting in [up-poll])
      • Options and defaults

        • Everything is opt-in
        • Navigation enables a set of new defaults
          • Navigation options are the only opinioniated defaults that are opt-out
        • config options
        • auto-options
        • Most options in up.render() can also be set via an attribute
          • JavaScript options override HTML attributes
          • If we cannot serialize an option into an attribute, you can use event.renderOptions in up:link:follow, up:form:sumit
        • Explain that you can often override renderOptions in event handlers
          • This is already explained in /render-hooks
      • Background requests

        • Background requests deprioritized over foreground requests.
        • Background requests also won't emit up:network:late events and won't trigger the progress bar.
        • Background requests are promoted to the foreground if they are a cache hit for a new, non-background request

      Icebox / Tar pits

      • Allow late registrations of compilers and macros without priority => OK for compilers, but what about macros? They have an intrinsic priority (before all compilers)

      • Consider whether up.validate() promises should wait until no more solutions are pending => We would need to merge RenderResult#target in some meaningful way

      • Rename "finished" to "concluded"

      • Should up:click set up.layer.current ?

        • It would be more convenient, but it's only relevant for popups or backdrop-less modals. This is rare.
      • New onTransitioned() callback to take some load off from onFinished()

      • Move scroll positions into state

        • This gets rid of the other up.Cache usage
        • This may mean we need to lose up.viewport.restoreScroll() and { scroll: 'restore' } and { saveScroll: true }
          • Losing { scroll: 'restore' } is super sad :( => Maybe revisit when the Navigation API is supported
      • Improve polling in the background

        • It would be great to not have a timeout running while we're in the background or offline
        • It would be great to not wait up to 10 seconds when we're re-focused or regain connectivity
          • Are timeouts really paused or do they just not fire until re-focus?
          • Mobile Chrome seems to reload old tabs automatically, test this!
      • Elemente mit [up-hungry][up-layer=any] müssten wir eigentlich auch austauschen, wenn wir einen neuen Layer öffnen

        • OpenLayer kann aber gar nicht mit mehreren Steps umgehen
      • can badResponseTime be a function that takes a request? => Yes, but not trivially

      • Consider using Cache-Control: stale-while-revalidate=<seconds> to indicate how long we can use a response

        • But it could be paired like this: Cache-Control: max-age=1, stale-while-revalidate=59
        • But then again we ignore Cache-Control for all other purposes
          • E.g. Cache-Control: no-store
          • E.g. Cache-Control: max-age
          • How would Cache-Control: no-cache (equivalent of max-age=0 must-revalidate) work in connection with up.fragment.config.autoRevalidate ?
        • Maybe do a bigger workover of Cache-Control?
      • Do follow links with [target=_self]

      • up:click forking could print a warning when an a[href] is instant, but not followable

      • Is it weird that up.layer.affix appends to the first swappable element instead of the contentElement?

        • It's actually more like "appendableElement"
        • Maybe offer up.Layer#append
      • Consider exposing up.layer.contentElement

      • Do we want a shortcut macro for this:

        • It's weird for users who don't target the input. They may expect to just override the event.
        • We would need to make keepable selectors configurable to include this one
      • We could support this with more pseudo-class selectors like :form-group and :submit-button

        • :submit-button is hard to build origin-aware => It could just be a substitution with :is() / :-moz-any() & :-webkit-any()
        • :form-group is also supper hard to support in a selector like ".foo, :form-group, :bar" due to the way we hacked :has()
          • :has() is still behind a flag in Chrome and no Firefox support
      • Introduce boundaries or "softly isolated zones"

        • The idea started with: Should fragment lookups with an { origin } within a form prefer to look within the form?
          • Also related to unpoly/unpoly#197 , which would no longer work now that a form submission's orgin is the submit button instead of the form element
        • E.g.
          • Lookups within prefer to match within the boundary
          • It's a new fallback target
            • Also for errors
          • up.fragment.config.boundaryTargets = ['[up-boundary]', 'form', ':main']
          • Is this also controlled by { fallback }?
          • Maybe identification using [up-boundary=123]
            • But don't enforce this, it's not a great auto-target
          • Should this rather be [up-zone]?
            • If we ever make fully isolated containers we would call them frames
          • We could also offer :zone as a selector
          • Would we still offer { target: '.container .child' }?
            • Would we offer { target: ':zone .foo' }, since it's really the same as { target: '.foo' } ?
          • Is this a repetition of "fragment needs to know whether it is used as component or main target"?
            • We would need to fix infinite looping in expandTargets()
            • It would be nice to disable history in a zone
              • but then it's not usable as a main target
              • Disable history in a container?
                • It's weird to nest multiple containerish elements
              • => This is really already solved through { history: 'auto' }, which only updates history if updating :main
      • Rendering: allow { scrollBehavior: 'smooth' } when we're not morphing

        • Could we even allow this when morphing?
      • New up.render() options to clone from a template

        • { documentTemplate }, { fragmentTemplate }, { contentTemplate }
        • Separate doc page "Rendering from local content"
        • Fail when template cannot be found
        • But what if I really need to re-use an existing element that is then placed back into the body, like in WB?
      • Consider implementing an abortable up:navigate event

        • This would be the first feature that goes beyond "navigation is just a preset"
        • People might expect history changes to trigger this
        • Maybe think about this more
      • Replace up.hello() and up.script.clean() with MutationObserver()

      • Do we want to serialize all actions in a form?

        • up-sequence="form"
        • This would need to abortable on submit => Would be handled by existing { solo: 'target' } IF there is a request
        • This would need to abortable on form destroy => Would be handled by existing { solo: 'target' } IF there is a request
        • => This would need to be handled by up.Queue, or else there would be nothing to abort
        • => It is not sufficient to have up.form.sequence(..., callback)
        • => We would need to do something like { sequence: ElementOfSubtree }
        • => Before picking a new request, make sure no existing request exists
        • What about our old idea: { order: 'abort target', order: 'abort form', order: 'after form', order: 'after selector' } => How to say "after ElementObject" ?
        • Who would fetch the element that is 'form' or 'selector'? => up.Change.UpdateLayer#getPreflightProps(), which already loads targetElements()
        • What would we do if both was given, e.g. { solo: true, sequence: 'form' }
          • Do we forbid the combination?
          • Do we first abort, then do a squence?
          • Do we first wait, then abort? => I think this, but this also means our { solo } handling is in the wrong place. It must move to the queue.
        • Does { sequence: 'form' } also queue local content, like { solo } ?
        • We could do something like up.LocalRequest, but then local updates would no longer be sync!
        • We could not support { sequence } for local updates => YES
        • What about cached content with { sequence }?
          • We could do queue.asapLocal() which immediately executes unless there is { sequence }
        • How does queue resolve a sequence?
          • Before dispatching a request with { sequence }
          • Check if we have current requests with { sequence }
          • If any of the other requests's sequence contains our or if any other sequence is contained by ours, don't dispatch
      • Guard Events for Rendering could have a Promise for "done"

        • Is this better than setting event.renderOptions.onFinished()?
          • Yes, because onFinished may not fire for fatals or prevented up:fragment:loaded
        • How would this work together with future up.RenderRequest?
        • How would this work together with "local changes are sync"?
      • Consolidate [up-validate], [up-switch] and [up-watch] into a tree of form dependencies

        • This way we can selectively disable parts of the form
      • Functionality that checks for isDetached() should probably also check for .up-destroying

      • Improve { focus: 'keep' } so it focuses the former form group if we lose focus

        • This may be easier said than done
          • we would need to remember the original form group before the swap in the FocusCapsule
          • re-discover the form group in the new HTML
          • check that the form group is a closer match than target-if-lost
          • come up for a better name for the option (target-if-lost)
      • New event up:request:solo ?

      • Consider delaying appending of new layer elements until we have constructed all children unpoly/unpoly#314

      • Publish { onQueued }

        • We're currently only using onQueued to get the request of a rander job, so we can abort it
          • This may be removed!
        • More canonic would be if RenderJob had an abort() method
      • Wir aborten bereits laufende [up-validate] wenn das Formular submitted, wird, aber laufende Watcher-Delays warten können danach noch Dinge tun

        • Wie wäre "submit stoppt das delay"? Evtl. Warnung ausbauen: "Will not watch fields without [name]"
      • [up-emit] auf Buttons erlauben

      • Beim Schließen des Modals prevented jemand up:layer:dismiss, und dann steht "Abort Error: Close event was prevented" in der Konsole.

        • Wollen wir das schlucken?
        • Zumindest bei ui-elementen wie [up-dismiss] ?
      • DestructorPass sammelt zwar Exceptions, aber wirft am Ende. Wer fängt das dann? Der Wunsch wäre, dass das drumrumliegende up.destroy() noch zu Ende läuft, dann aber up.CompilerError wirft.

      • ConstructorPass sammelt zwar Exceptions, aber wirft am Ende. Wer fängt das dann? Der Wunsch wäre, dass das drumrumliegende up.render() oder up.hello() noch zu Ende läuft, dann aber mit up.CompilerError rejected.

      • Update "Long Story" Slides with new API

      • Doc page about "Fragments from local HTML"

        • link from [up-document], [up-fragment], [up-content], { document, fragment, content }.
      • Warn when registering compiler in [up-] namespace

      • Consider documenting errors

        • But what would be the @parent ?
        • up.CannotCompile
        • up.CannotMatch
        • up.Offline
        • up.AbortError
          • has { name: 'AbortError' }
      • Should the old "clear" be "expire" or "evict"? => We really want to push our new defaults for both => I think it should be "expire". Most users set a lower expire time.

      • remove up.util.flatMap() => No, we need it to flatMap array-like objects (e.g. arguments)

        • Do we want to move to saveState() / restoreState()?
          • I think we want to keep the [up-focus] and [up-scroll] options separate.
            • E.g. we want to focus the main element, but reset scroll.
            • This could also be fixed by revealSnap ?
          • These are eerily similar:
          • What would be the name for such an attribute?
            • [up-spotlight]
            • [up-viewport] (classes with [up-viewport]
            • [up-highlight]
            • [up-locus]
            • [up-present]
            • [up-light]
            • [up-shine]
            • [up-state] (seltsam: up-state=".element")
            • [up-point]
            • [up-pinpoint]
            • [up-attention]
            • [up-focus] also scrolls?
            • [up-show]
            • [up-view] => I think power users want to control this separately => Also we need to call it at different times => Also the auto options work differently, e.g. if there is an [autoscroll] element in the new fragment => We might offer a shortcut like [up-view] and [up-save-view] as a shortcut to set both at once
      • Replace up.rails by accepting methods / conform from data attributes in options parser => This wouldn't work in scenarios where both Rails UJS and Unpoly were active

      • No longer send X-Up-Location with every response => No we should keep sending it, as this excludes redirect-forwarded up-params

      • Consider reverting: up:request:late: Consider the time a promoted request was loading in the background => For this we would need to track when a request was promoted to the foreground

      • Do we trigger onFinished when preloading? => No, users can use the promise or onLoaded()

      • Reconsider how many lifecycle callbacks we want to parse from links

        • Benchmark up.link.followOptions()
          • console.time('parse'); for (var i = 0; i < 10000; i++) { up.link.followOptions(link) }; console.timeEnd('parse')
          • VM481:1 parse: 1091.6689453125 ms => It takes 0.1 ms => This is not a performance issue
      • Find a scenario where it's better to read the etag from [etag] instead of response.etag

        • This should not matter for revalidation after a swap
        • When reloading an arbitrary fragment, an earlier response may not be available
      • Test if browsers honor cache keys for XHR requests

        • Yes, it honors Cache-Control: max-age=...
        • We can override it for fetch()
      • With our long cache eviction background tabs could hog a lot of memory => No, since we also limit the number of cache entries to the cache never exceeds some MB

      • up.link.cacheState(link) to people can build .offline classes themselves => Users can already use up.cache.get => We could clean up Request#state and publish this for more goodness

        • Return any known up.Respone for the given link
          • It already has useful properties { evictAge }, { expireAge }?
          • We may eventually offer up.Response#revalidate()
        • Return null while the request is in flight => Or do we want an up.CacheState that also returns here?
        • It will be hard to do implement this without actually calling up.link.follow() and up.render(), since e.g. the target choice is hard and part of the cache key
          • Make this an early return in up.Change.FromURL, like with options.preload
      • Test if we can preserve element.upPolling (or at least the { forceState }) through up.reload({ data }) => It's hard since there's the case that the server no longer responds with [up-poll], but we want to keep polling when forceStarted

      • Should we delete options.data if a fallback is loaded?

        • This would also be true for other options, like a selector in focus/scroll
        • We would sometimes need to guess, e.g. { focus: ':main' } may also be a good default for a fallback => I think this is not a real case since we're not going to use { data } together with { fallback }
      • Consider moving [up-etag], [up-time] and related functions to up.protocol => While there's some merit to that, we would need to rename up.fragment.etag() to up.protocol.fragmentETag() etc. => What's with up.fragment.source()?

      • [up-hungry][up-placement] => This only makes sence when we have some form of deduping

      • What do we log when no replacement target is available? => up.render() throws an error

      • Reconsider why we don't call onRendered() for empty updates

        • Pro: Call it for every pass
          • We do still update scroll and focus, even for empty responses
            • Maybe even location?
          • We will update more in the future with head merging
          • There is no other hook for "after $things changed"
            • But nothing changed after an empty render? At least not in the DOM.
          • We can simplify docs for onRender and up-on-render
        • Con: Keep it like now
          • If we render without fallback we still need to check if up.RenderResult#fragment is defined
          • It will be super rare that the first render pass does not return anything.
            • However, we would always get empty onRendered calls for revalidation => Keep it, but improve docs
      • Maybe ship empty unpoly.es5.js und unpoly.es5.min.js that just say to use ES6 => Not worth the hassle

      • Does it make sense to have onKeep in render options? => I find it confusing to have both { onKeep } and { useKeep } in the same functions => Maybe we can find a way to merge it with { useKeep }

      • Consider up.layer.config.isolateLabels => Wait until there is a use case => Users can always just intercept click themselves

      • Consider [aria-haspopup] => This would require a compiler automatically setting this for [up-layer=new] links (and all shortcuts) => Don't do this for performance. Delegate to apps.

      • Should config.autoCache allow to cache error responses? => That's a hard question to answer

      • Get rid of response.request (which contains references to a layer), response.xhr ? => This is hard because up.cache.expire() expires requests, not responses. Hence up.Response delegates { expired } to its response.

      • Can we allow await in Callbacks?

        • new AsyncFunction()
        • This would not work trivially with the postprocessing in up.NonceableCallback
          • At least we would need to detect use of await
      • Returning 'auto' for default would be a good way to override configs with exceptions

        • request.url.endsWith('/edit') ? false : 'auto'
        • Since this already the 'autoMeans' config we cannot return 'auto' here
      • Replace u.flatten() with Array#flat() => We keep array-related utils because ours work with non-Array lists and iterators

      • Replace u.flatMap() with Array#flatMap() => We keep array-related utils because ours work with non-Array lists and iterators

      • Now that compilers have the meta arg it should be possible to give up.RenderResult new { request, response } props => Wait until we have a use case for this

      • Test synthetic ETag

      • When rendering uses elements, do we set [up-time] and [up-etag] on these wrappers?

        • We cannot really do better here, since up-wrapper children cannot be reloaded
      • Move isRenderableLayer() logic to LayerLookup

        • Consider having LayerLookup#getAll() throw if results are blank => We have a lot of code / specs that work with closed layers and sometimes normalize the layer through up.layer.get(), and now get undefined. All of this code would fail with this change.
      • Terminology for notifications

        • [up-alerts] (Bootstrap, Tailwind UI, MUI, negative connotation in Material etc.)
        • [up-flashes] (Rails, Primer [although they also use Toast])
        • [up-notifications] (Bulma, good for both success and failure, long word, may also mean passive notifications)
        • [up-messages] (too generic)
        • [up-snackbars] (Material, uncommon)
        • [up-toasts] (uncommon, also often a "boxy" kind of notification)
        • [up-callouts] (Foundation, uncommon)
      • Do we want to support changing the renderOptions in up:fragment:hungry? In that case { renderOptions } need to be { nonHungryRenderOptions } or something => We can rename the options then. If we do support this we would no longer have { renderOptions }, we would use { targetRenderOptions, hungryRenderOptions }

      • Instead of ResponseDoc#commitSteps(), could up.radio.hungrySteps() just re-compress steps with uniqBy(steps, 'selector')? => There is an edge case where different selectors could match the same elemnent

      • Introduce up:history:restore event

        • With { renderOptions }
        • Note that we used to emit this event in the past, so fix unpoly-migrate
        • Preventable for custom history handling => We already have this in up:location:restore
      • Reduce render callbacks

        • Motivation
          • It is too hard to do something when the render job settles. There is onRendered, onFinished, onError etc. However there are always cases that are covered by no callback.
          • We need to maintain and communicate two styles of callbacks
        • Expose { renderJob } on guard events
        • New event up:fragment:render / [up-on-render] that gets { renderOptions, renderJob }
          • There is no need to offer { onRender }, as the return value of up.render() is already a RenderJob
        • Deprecate callbacks
          • { onFinished }
          • { onRendered }
        • Update docs
      • Do we need a way to show a spinner for a while?

        • Maybe, but you can already do in CSS: .container:has(.up-active) .spinner { ... }
      • Do we really need to abort requests in LinkPreloader? => Yes, aborted requests can be seen in the network tab. We also found a shorter way to write it.

      • Allow the [wants-menu] compiler to be implemented without JS

        • We would need something like [up-defer][up-unless-already-there]
          • Or even a follow option?
          • Should we just check, or "only revalidate"?
          • Or should it be the default behavior? => This is really hard to do right
      • Do we dare to restore history using up.render() instead of up.navigate()? => We override as much as want to re-use

      • Do we want [up-skeleton=] ?

        • We would need to decide what to do within overlays
          • Show skeleton within overlay?
          • Animation
          • Different skeleton => Later
      • Consider renaming [up-feedback] to something else

        • Maybe always have classes?
          • This would mirror the exception of background, which is also false by default => Delay this until we need the setting for something else
      • Consider offering a { renderLayer } prop to guard events

        • This would make it easier to e.g. auto-add a preview for new layers in up:link:follow
        • Do we need to give a better name to { layer } so people understand it is not the origin layer?
          • There is { targetLayer }, but that could be confused with event.target (which happens to be the origin layer!)
          • { changeLayer }
          • { updateLayer }
          • { targetedLayer }
          • { planLayer }
          • { insertLayer }
          • { renderLayer }
        • Update docs for guardEvents
          • up:link:follow [yes]
          • up:form:submit [yes]
          • up:link:preload [yes]
          • up:deferred:load [makes no sense]
          • up:form:validate [makes no sense]
          • up:fragment:poll [makes no sense] => No, this becomes very complicated very fast
      • Consider whether { skeleton } is flexible enough for public API

      • Deprecate flatten() in favor of Array#flat(), flatMap() in favor of Array#flatMap()

        • No, we use this a lot of non-array values, in particular NodeLists
      • Do we want to keep { feedback }, [up-feedback] as an option, or applied it always?

        • It's already a navigation default
        • For programmatic up.render() calls, it's already activated by passing { origin } => We can decide this later
      • Do we want Preview#affix?

        • Check if it helps with the demo => Not for now. I'm myself moving away from affix.
      • Previews should know about placement?

        • Unsure. Later.
      • Rename "preview" to "teaser"?

        • => It's too confusing with { placeholder }
      • Test that re-attaching an helloing an destroyed element can be compiled again

        • This is not trivial because destructors would need to clean up Element#upAppliedCompilers
      • Move config.progressBar to up.status ?

        • Is it weird that up:network:late and up:network:recover events are still in the up.network package?
        • We also keep [up-disable] under up.form => Keep it there

About

Tasks and design notes for Unpoly

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published