rewind now captures only inputs that the page can put
back. A plot's hover, click, dblclick and brush, and the values
that DT keeps, such as _rows_current and _state, have no element in the
page to restore them. rewind recorded them anyway, so each move of the
mouse over a plot was a step, and an undo of it seemed to do nothing. The
browser now reports which inputs have a Shiny binding, and only those are
captured. A list in inputs is used as it is, so name an input there to
capture it anyway. Until the report arrives, and in testServer(), every
input is captured as before.rewind_status() lists what rewind captures, and gives each other input
the reason it is left out: excluded, a password field, not in inputs,
cannot be restored by the page, and so on. It answers "why does an undo
skip this input?" in one call.rewind_exclude() leaves inputs out of the history from anywhere in the
server, a module included, and at any time. It works like
shiny::setBookmarkExclude(): inside a module, give the module's own
names. A second call adds to the list.rewind_unrecorded() runs a block of code whose changes are not a step.
They become part of the current state, so an undo never goes back across
them. Use it for code that restores a saved view or applies a bookmark.
It is the other side of rewind_step().rewind_enable(labels = ) gives the names a person reads, such as
c(region = "Region"). The history rail shows them, and rewind_diff()
has a new label column. It is the last column, so code that picks the
six columns of 0.3.0 by position still gets the same ones.rewind_pause() suggested exactly that, around code that applies
a bookmark. rewind captures in its own observer, which runs after the
one that made the change. The help now says so, and points to
rewind_unrecorded().shortcuts = "yes" (which switched the shortcuts off),
depth = 2.5 (which became 2), rewind_jump(1.9) (which moved to
position 1), rewind_step(label = 1:2), and rewind_ui(max_height = 300),
which wrote CSS that browsers ignore. A number is now pixels there, as in
shiny and bslib.inputs that the page does not have, and a name in
rewind_track(fields = ) that the reactive values do not have, now give
a warning. A typo used to capture nothing, with no message.rewind_enable(c("region", "year")) gave the names to session, the
first argument, and failed with "$ operator is invalid for atomic
vectors". It now says that session must be a Shiny session, and points
to inputs = c(...).passwordInput() sends a
plain string, so rewind captured it like any text box: the password was
kept in the history, put back in the field by an undo, and shown in plain
text by rewind_diff(). The browser now reports which fields are
passwords, including fields that renderUI() adds later, and rewind
leaves them out, even when inputs names them. A value captured before
the report arrives is removed from the history.rewind_diff() shows an empty text box as "". A change from an empty
box read -> hello, with nothing before the arrow.rewind_enable() has a new section on text inputs:
slow typing can give several steps, textInput(updateOn = "blur") gives
one step for each edit, and Ctrl + Z inside a text box is the
browser's own undo, which rewind then records as a new step.rewind.js that waits for Shiny to connect used
addEventListener(), which never receives the jQuery event that Shiny
triggers. It now uses jQuery. The fallback is not used when Shiny loads
first, which is the usual order.This release goes to CRAN. Version 0.2.1 was released on GitHub only, so an update from 0.2.0 on CRAN brings the changes of 0.2.1 below as well.
New rewind_diff(). It shows what changed between two steps, and not only
which names changed. [rewind_history()] gives a label such as
"region, year"; rewind_diff() gives one row for each value, with the
value before, the value after, and a short description such as
"North -> South". The old and new columns are list columns, so a
value of any type survives, including a date range, a multiple selection
or a whole data frame. The result depends on the history, so it works
inside render*() and observe().
It is not an audit trail, and the documents say so. The history is in the
memory of one session, it drops the oldest entries when it passes depth,
and it drops the steps in front of the position after a change. Write a
record for an inspection as each change occurs, and keep it elsewhere.
The history rail no longer scrolls the page or the sidebar that holds it.
It kept the current step in view with scrollIntoView(), which scrolls
every scrollable ancestor of the element and not only the nearest one. In
a layout that puts rewind_ui() in a sidebar, every change to the history
scrolled that sidebar, so the application appeared to scroll by itself.
The rail now sets its own scrollTop, and moves nothing else.
Fixed: undo, redo and the rail did nothing when rewind_enable() was
called inside a moduleServer(). The browser sends the global input ids
rewind_undo, rewind_redo and rewind_jump, but inside a module
rewind_enable() watched the namespaced ids. Capture still worked, so the
rail filled up, but no button, shortcut or rail click reached the history.
rewind_enable() now reads these inputs from the root session.
Fixed: the rail showed each time in UTC, and not in the viewer's time
zone. The server now sends the moment, and the browser formats it with
its own clock. The format is still HH:MM:SS. The time column of
rewind_history() is unchanged.
rewind_enable() and rewind_step() now refuse NA, NaN and Inf
in coalesce_ms, restore_timeout and hold_ms, with a message that
names the argument. NA gave "missing value where TRUE/FALSE needed".
Inf was accepted and then did harm with no message: in coalesce_ms it
stopped the history from recording anything. hold_ms was not checked
at all before.
rewind_diff() now refuses a position that is not a whole number, such as
2.7. It truncated it to 2 before.
rewind_enable() takes a restore_timeout argument, in seconds. It sets
how long rewind waits for the browser to finish a restore. The value
was fixed at 2 seconds before, which is enough on a fast connection but
not always on a slow one. If the limit is too short, capture starts again
while the browser is still applying the restore, and a partial state
becomes a history entry that the user never made.rewind no longer drops a data frame of the user that has the same four
column names as the value of a fileInput(). It now examines the column
types as well as the names. Such a value was dropped from the history
before, with no message.rewind_buttons() takes a button_class argument. It adds CSS classes to
the two buttons. Use it for a Bootstrap variant such as "btn-primary" or
"btn-outline-secondary", and for a size such as "btn-sm". The class
argument continues to hold classes for the container element only, so
there was no way to reach the buttons before.↶ and ↷, which come from the font of the browser and thus
have a different weight and size on each system. The SVG arrows use
currentColor and a size in em. They thus follow the colour and the
size of the button, including with btn-sm, btn-lg, an outline variant
and a dark theme.Ctrl + Y has
always done a redo, but the button did not say so.shortcuts argument of rewind_enable() now
lists the three shortcuts, and says which one belongs to which system.rewind_disable() stops undo and redo for a session completely. It
destroys each observer that rewind_enable() made. It also clears the
buttons and the rail in the browser. Use rewind_pause() and
rewind_resume() to stop capture for a short time. Use
rewind_disable() to stop it for the rest of the session.rewind no longer captures fileInput(). Its value points to a
temporary file on the server. Shiny deletes that file at the next upload.
An old snapshot would thus point to a file that does not exist.rewind_pause() before the first reactive flush stopped
capture for the rest of the session. A later rewind_resume() did not
start it again. This occurred, for example, with a call to
rewind_pause() immediately after rewind_enable(), before any input
changed.rewind_buttons(undo_label = NULL, redo_label = NULL) now sets
aria-label on the buttons. A button with an icon and no text thus has a
correct accessible name.rewind_enable() inside a moduleServer() now has tests. The documents
said "untested" before.First release.
rewind_enable() captures the session inputs into an undo and redo
history. It groups the changes by time. One movement of a slider is thus
one step, and not forty steps.rewind_track() adds the reactiveValues on the server to the history.rewind_step() puts several changes into one entry with a label.rewind_buttons() and rewind_ui() give ready-made undo and redo
controls, and a scrubbable history rail.Ctrl/Cmd + Z, Ctrl/Cmd + Shift + Z, and
Ctrl + Y. They do nothing while the user types in a text field.update*Input() table. Inputs from other packages thus operate with no
extra code.