Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ Notable upstream projects (non-exhaustive; see inventory for pins and status):
orbs for AI and agent status, wrapped as dlc.thinking_orbs.Orb. Not to be
confused with the unrelated @yogesharc/thinking-orbs package, which is not
wrapped.
- dlc.lab is PhylaTech's own work (MIT, this repo): two SVG spinners, HplcTrace
and WellPlate. No third-party code or assets.

Hard skips (not wrapped): css-spinners (GPL-3.0), react18-loaders (MPL-2.0).

Expand Down
30 changes: 25 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# dash-loading-components (`dlc`)

Loading indicators for [Plotly Dash](https://dash.plotly.com), wrapping thirteen
React spinner libraries as one install. 470 components under stable namespaces,
React spinner libraries, plus a family of our own, as one install. 474 components under stable namespaces,
with a common factory over the top so you can switch families without
relearning each one's props.

Expand Down Expand Up @@ -44,7 +44,7 @@ dcc.Loading(children, custom_spinner=dlc.ldrs.Ring(size=48))
Discover what is available at runtime:

```python
dlc.list_libraries() # ['loading_dev', 'ldrs', 'spinners', ..., 'gridora', 'loadersz', 'thinking_orbs']
dlc.list_libraries() # ['loading_dev', 'ldrs', 'spinners', ..., 'gridora', 'loadersz', 'thinking_orbs', 'lab']
dlc.list_spinners("ldrs") # ['Ring', 'Helix', 'DotPulse', ...]
dlc.list_spinners("loadersz") # ['working', 'searching', 'connecting', ...]
```
Expand Down Expand Up @@ -72,17 +72,18 @@ resolve it; it will accept the final 4.5 release once published.
| `spinners_react` | [spinners-react](https://www.npmjs.com/package/spinners-react) | 9 |
| `epic` | [react-epic-spinners](https://www.npmjs.com/package/react-epic-spinners) | 8 |
| `m3` | [@alerix/m3-loading-indicator](https://www.npmjs.com/package/@alerix/m3-loading-indicator) | 1 |
| `flicker` | [flicker-dot](https://www.npmjs.com/package/flicker-dot) | 61 |
| `flicker` | [flicker-dot](https://www.npmjs.com/package/flicker-dot) | 63 |
| `gridora` | [gridora](https://www.npmjs.com/package/gridora) | 133 |
| `loadersz` | [loadersz](https://www.npmjs.com/package/loadersz) | 160 |
| `thinking_orbs` | [thinking-orbs](https://www.npmjs.com/package/thinking-orbs) | 9 |
| `lab` | PhylaTech originals (SVG, no upstream package) | 2 |

## Flip-dot presets

flicker-dot is a player: it animates whatever 7x7 frames it is given. `dlc.flicker`
ships 61 original presets for it, drawn by PhylaTech and MIT licensed with this
ships 63 original presets for it, drawn by PhylaTech and MIT licensed with this
package, in six sets: Field (the naturalist set: `Mycelium`, `Diatom`,
`Cladogram`, `Chromatogram`, `MassSpec`, `TestTube`, `Jellyfish`, ...), Data
`Cladogram`, `Chromatogram`, `HplcTrace`, `WellPlate`, `MassSpec`, `TestTube`, `Jellyfish`, ...), Data
(`Scatter`, `Histogram`, `LineChart`, `PieChart`, `Heatmap`, `Database`),
Everyday (`Cart`, `Upload`, `Download`, `Mail`, `Wifi`, `Heart`, `Lock`, ...),
Board (flip-dot panel classics), Geometry, and Solids (the five Platonic
Expand Down Expand Up @@ -189,6 +190,25 @@ Version pins, SPDX licenses, upstream URLs and React peers:
[docs/UPSTREAM-INVENTORY.md](docs/UPSTREAM-INVENTORY.md). Attribution:
[NOTICE](NOTICE).

## Lab spinners

`dlc.lab` is PhylaTech's own: SVG drawings animated with plain CSS, wrapping no
upstream package. `dlc.lab.HplcTrace` is the detector trace of an HPLC or UPLC
run, as the instrument feeding a mass spectrometer draws it: a pen sweeps the
baseline across retention time, compounds elute as peaks of their own height,
and each apex is marked as it passes. `dlc.lab.WellPlate` is a 96-well
microplate (8 rows by 12 columns, A1 corner cut off) that a pipette fills well
by well, then empties in the same order. (`dlc.flicker.HplcTrace` and
`dlc.flicker.WellPlate` are the 7x7 dot-matrix takes on the same two.)

```python
dlc.lab.HplcTrace(size=96, color="#f97316")
dlc.lab.WellPlate(size=96, rate=1.5)
```

Both take `size` (the width in pixels), `color`, `on_opacity`, `rate` and
`playing`; `WellPlate` also takes `off_color` and `off_opacity` for its empty wells.

## Relative tempo

`rate` is a rate, not a duration. `1.0` reproduces the tempo the upstream
Expand Down
1 change: 1 addition & 0 deletions assets/gallery.css
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ p { text-wrap: pretty; }
.dlc-icon-gridora { --dlc-icon: url(icons/gridora.svg); }
.dlc-icon-loadersz { --dlc-icon: url(icons/loadersz.svg); }
.dlc-icon-thinking_orbs { --dlc-icon: url(icons/thinking_orbs.svg); }
.dlc-icon-lab { --dlc-icon: url(icons/lab.svg); }
.dlc-icon-overview { --dlc-icon: url(icons/overview.svg); }
.dlc-icon-credits { --dlc-icon: url(icons/credits.svg); }
.dlc-icon-request { --dlc-icon: url(icons/request.svg); }
Expand Down
1 change: 1 addition & 0 deletions assets/icons/lab.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
88 changes: 88 additions & 0 deletions dash_loading_components/LabHplcTrace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# AUTO GENERATED FILE - DO NOT EDIT

import typing # noqa: F401
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
from dash.development.base_component import Component, _explicitize_args
try:
from dash.types import NumberType # noqa: F401
except ImportError:
# Backwards compatibility for dash<=4.1.0
if typing.TYPE_CHECKING:
raise
NumberType = typing.Union[ # noqa: F401
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
]

ComponentSingleType = typing.Union[str, int, float, Component, None]
ComponentType = typing.Union[
ComponentSingleType,
typing.Sequence[ComponentSingleType],
]


class LabHplcTrace(Component):
"""A LabHplcTrace component.
LabHplcTrace — the detector trace of an HPLC or UPLC run, the kind that
feeds a mass spectrometer: a pen draws the baseline across the retention
time axis, compounds elute as peaks of their own height, and each apex is
marked as it passes. Drawn as SVG and CSS; dlc.flicker.HplcTrace is the
7x7 dot-matrix take on it.

Keyword arguments:

- id (string; optional):
The ID used to identify this component in Dash callbacks.

- aria_label (string; optional):
Accessible name, announced to assistive technology (default
\"Loading\").

- className (string; optional):
CSS class applied to the outer wrapper.

- color (string; optional):
Color of the trace, the pen and the axes (default currentColor).

- on_opacity (number; optional):
Opacity of the trace, 0 to 1 (default 1).

- playing (boolean; optional):
Set to False to pause the animation.

- rate (number; optional):
Relative tempo: 1.0 is this spinner's own tempo (a run in 7
seconds), 2.0 twice as fast, 0.5 half.

- size (number; optional):
CSS pixel width (default 64); the height follows the chart."""
_children_props: typing.List[str] = []
_base_nodes = ['children']
_namespace = 'dash_loading_components'
_type = 'LabHplcTrace'


def __init__(
self,
id: typing.Optional[typing.Union[str, dict]] = None,
className: typing.Optional[str] = None,
style: typing.Optional[typing.Any] = None,
size: typing.Optional[NumberType] = None,
color: typing.Optional[str] = None,
on_opacity: typing.Optional[NumberType] = None,
aria_label: typing.Optional[str] = None,
rate: typing.Optional[NumberType] = None,
playing: typing.Optional[bool] = None,
**kwargs
):
self._prop_names = ['id', 'aria_label', 'className', 'color', 'on_opacity', 'playing', 'rate', 'size', 'style']
self._valid_wildcard_attributes = []
self.available_properties = ['id', 'aria_label', 'className', 'color', 'on_opacity', 'playing', 'rate', 'size', 'style']
self.available_wildcard_properties = []
_explicit_args = kwargs.pop('_explicit_args')
_locals = locals()
_locals.update(kwargs) # For wildcard attrs and excess named props
args = {k: _locals[k] for k in _explicit_args}

super(LabHplcTrace, self).__init__(**args)

setattr(LabHplcTrace, "__init__", _explicitize_args(LabHplcTrace.__init__))
97 changes: 97 additions & 0 deletions dash_loading_components/LabWellPlate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# AUTO GENERATED FILE - DO NOT EDIT

import typing # noqa: F401
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
from dash.development.base_component import Component, _explicitize_args
try:
from dash.types import NumberType # noqa: F401
except ImportError:
# Backwards compatibility for dash<=4.1.0
if typing.TYPE_CHECKING:
raise
NumberType = typing.Union[ # noqa: F401
typing.SupportsFloat, typing.SupportsInt, typing.SupportsComplex
]

ComponentSingleType = typing.Union[str, int, float, Component, None]
ComponentType = typing.Union[
ComponentSingleType,
typing.Sequence[ComponentSingleType],
]


class LabWellPlate(Component):
"""A LabWellPlate component.
LabWellPlate — a 96-well microplate (8 rows by 12 columns, A1 corner
cut off) that a pipette fills well by well, row after row, until it is full.
It then empties in the same order, and the pipette only comes back once every
well is empty. Drawn as SVG
and CSS; dlc.flicker.WellPlate is the 7x7 dot-matrix take on it.

Keyword arguments:

- id (string; optional):
The ID used to identify this component in Dash callbacks.

- aria_label (string; optional):
Accessible name, announced to assistive technology (default
\"Loading\").

- className (string; optional):
CSS class applied to the outer wrapper.

- color (string; optional):
Color of the liquid, the pipette and the plate outline (default
currentColor).

- off_color (string; optional):
Color of an empty well, drawn at `off_opacity` (default `color`).

- off_opacity (number; optional):
Opacity of the empty wells, 0 to 1 (default 0.16).

- on_opacity (number; optional):
Opacity of the liquid, 0 to 1 (default 1).

- playing (boolean; optional):
Set to False to pause the animation.

- rate (number; optional):
Relative tempo: 1.0 is this spinner's own tempo (a plate in about
13 seconds), 2.0 twice as fast, 0.5 half.

- size (number; optional):
CSS pixel width (default 64); the height follows the plate."""
_children_props: typing.List[str] = []
_base_nodes = ['children']
_namespace = 'dash_loading_components'
_type = 'LabWellPlate'


def __init__(
self,
id: typing.Optional[typing.Union[str, dict]] = None,
className: typing.Optional[str] = None,
style: typing.Optional[typing.Any] = None,
size: typing.Optional[NumberType] = None,
color: typing.Optional[str] = None,
on_opacity: typing.Optional[NumberType] = None,
off_color: typing.Optional[str] = None,
off_opacity: typing.Optional[NumberType] = None,
aria_label: typing.Optional[str] = None,
rate: typing.Optional[NumberType] = None,
playing: typing.Optional[bool] = None,
**kwargs
):
self._prop_names = ['id', 'aria_label', 'className', 'color', 'off_color', 'off_opacity', 'on_opacity', 'playing', 'rate', 'size', 'style']
self._valid_wildcard_attributes = []
self.available_properties = ['id', 'aria_label', 'className', 'color', 'off_color', 'off_opacity', 'on_opacity', 'playing', 'rate', 'size', 'style']
self.available_wildcard_properties = []
_explicit_args = kwargs.pop('_explicit_args')
_locals = locals()
_locals.update(kwargs) # For wildcard attrs and excess named props
args = {k: _locals[k] for k in _explicit_args}

super(LabWellPlate, self).__init__(**args)

setattr(LabWellPlate, "__init__", _explicitize_args(LabWellPlate.__init__))
3 changes: 2 additions & 1 deletion dash_loading_components/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@


# Family namespaces (short names): dlc.loading_dev.Arc, dlc.spinners.ClipLoader, …
from . import loading_dev, ldrs, spinners, spinners_react, loader_spinner, premium, indicators, m3, epic, flicker, gridora, loadersz, thinking_orbs
from . import loading_dev, ldrs, spinners, spinners_react, loader_spinner, premium, indicators, m3, epic, flicker, gridora, loadersz, thinking_orbs, lab

from .registry import list_libraries, list_spinners

Expand All @@ -106,6 +106,7 @@
"gridora",
"loadersz",
"thinking_orbs",
"lab",
"list_libraries",
"list_spinners",
]
4 changes: 4 additions & 0 deletions dash_loading_components/_imports_.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from .IndicatorsSlab import IndicatorsSlab
from .IndicatorsThreeDot import IndicatorsThreeDot
from .IndicatorsTrophySpin import IndicatorsTrophySpin
from .LabHplcTrace import LabHplcTrace
from .LabWellPlate import LabWellPlate
from .LdrsDotPulse import LdrsDotPulse
from .LdrsDotWave import LdrsDotWave
from .LdrsHelix import LdrsHelix
Expand Down Expand Up @@ -134,6 +136,8 @@
"IndicatorsSlab",
"IndicatorsThreeDot",
"IndicatorsTrophySpin",
"LabHplcTrace",
"LabWellPlate",
"LdrsDotPulse",
"LdrsDotWave",
"LdrsHelix",
Expand Down
34 changes: 17 additions & 17 deletions dash_loading_components/dash_loading_components.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dash_loading_components/dash_loading_components.min.js.map

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions dash_loading_components/flicker/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ def component(**kwargs) -> Spinner:
Mitosis = _preset("Mitosis")
Seedling = _preset("Seedling")
Chromatogram = _preset("Chromatogram")
HplcTrace = _preset("HplcTrace")
WellPlate = _preset("WellPlate")
Scatter = _preset("Scatter")
Histogram = _preset("Histogram")
PieChart = _preset("PieChart")
Expand Down
37 changes: 37 additions & 0 deletions dash_loading_components/flicker/presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,41 @@ def chromatogram():
return frames + [frames[-1], frames[-1]]


def hplc_trace():
# A detector trace scrolling off an HPLC run, as the chart recorder on an
# LC-MS stack draws it: the pen sits at the right edge, a tall compound
# elutes, then a small one, and the baseline runs flat between them. Unlike
# Chromatogram's TLC plate, peaks arrive in time rather than climb in space.
heights = [0, 1, 3, 5, 3, 1, 0, 0, 1, 2, 1, 0]
period = len(heights)
frames = []
for offset in range(period):
# Each column spans from its own sample halfway to each neighbor's, so
# the flanks of a peak stay joined on the way up and on the way down.
tops = [6 - heights[(c + offset - 1) % period] for c in range(-1, N + 1)]
cells = set()
for c in range(N):
prev, top, nxt = tops[c], tops[c + 1], tops[c + 2]
rows = (top, (top + prev) // 2, (top + nxt) // 2)
cells |= {(r, c) for r in range(min(rows), max(rows) + 1)}
frames.append(cells)
return frames


def well_plate():
# A liquid handler working a microplate: its pipette head rides above the
# plate and fills each well in turn, row after row, until the plate is full.
# Then the rows empty top to bottom and the head parks for the next plate.
wells = [(r, c) for r in range(2, 6) for c in range(N)]
frames = []
for i, (r, c) in enumerate(wells):
frames.append(set(wells[: i + 1]) | {(0, c), (1, c)})
full = set(wells)
frames += [full] * 3
frames += [{w for w in wells if w[0] >= r} for r in range(3, 6)]
return frames + [{(0, 0), (1, 0)}]


# ---------------------------------------------------------------------------
# Data: charts drawing themselves, for dashboards waiting on a query
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -799,6 +834,8 @@ def dot(x, y):
("Mitosis", "field", mitosis, "A cell pinching in two and the daughter cells drawing back together."),
("Seedling", "field", seedling, "A seed in the soil sending up a shoot that opens its first leaves."),
("Chromatogram", "field", chromatogram, "Thin-layer chromatography: spots climbing three lanes at their own retention factors."),
("HplcTrace", "field", hplc_trace, "A liquid chromatography detector trace scrolling by, compounds eluting as peaks of their own height."),
("WellPlate", "field", well_plate, "A pipette filling a microplate well by well, row after row, until the plate is full."),
("Scatter", "data", scatter, "Points landing one by one along a rising trend."),
("Histogram", "data", histogram, "Bins filling left to right into a bell curve, then emptying."),
("PieChart", "data", pie_chart, "A pie filling slice by slice from twelve o'clock, then emptying the same way round."),
Expand Down
31 changes: 31 additions & 0 deletions dash_loading_components/lab/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Namespace for dlc.lab -- PhylaTech's own SVG spinners for science waits.

Unlike the other families these wrap no upstream package: each is drawn here as
SVG and animated with plain CSS, so ``rate`` and ``playing`` need no timer.

``HplcTrace``
The detector trace of an HPLC or UPLC run, the kind that feeds a mass
spectrometer: a pen draws the baseline across retention time, compounds
elute as peaks of their own height, and each apex is marked as it passes.
``WellPlate``
A 96-well microplate (8 rows by 12 columns, A1 corner cut off) that a
pipette fills well by well, then empties in the same order.

Usage::

import dash_loading_components as dlc

dlc.lab.HplcTrace(size=96, color="#f97316")
dlc.lab.WellPlate(size=96, rate=1.5)
"""

from ..LabHplcTrace import LabHplcTrace as HplcTrace
from ..LabWellPlate import LabWellPlate as WellPlate

# name -> blurb, in gallery order.
SPINNERS = {
"HplcTrace": "A liquid chromatography detector trace drawn across retention time, compounds eluting as peaks of their own height.",
"WellPlate": "A pipette filling a 96-well microplate well by well, row after row, then emptying it in the same order.",
}

__all__ = ["HplcTrace", "WellPlate", "SPINNERS"]
Loading
Loading