@zumer/orbit-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +395 -0
- package/package.json +57 -0
- package/src/base.js +133 -0
- package/src/o-clock.js +383 -0
- package/src/o-compass.js +115 -0
- package/src/o-donut.js +100 -0
- package/src/o-gauge.js +135 -0
- package/src/o-instrument.js +280 -0
- package/src/o-knob.js +191 -0
- package/src/o-menu.js +280 -0
- package/src/o-meter.js +109 -0
- package/src/o-radar.js +145 -0
- package/src/o-rings.js +93 -0
- package/src/orbit-kit.js +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Juan Martin Muda — Zumerlab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# @zumer/orbit-kit
|
|
2
|
+
|
|
3
|
+
High-level, declarative web components built on top of the
|
|
4
|
+
[Orbit](https://github.com/zumerlab/orbit) CSS framework.
|
|
5
|
+
|
|
6
|
+
Orbit gives you **primitives** — a CSS engine for radial layout. It's powerful,
|
|
7
|
+
but composing a single widget means nesting five levels
|
|
8
|
+
(`.bigbang > .gravity-spot > .orbit-N > .satellite > .capsule`) and getting the
|
|
9
|
+
rules exactly right. That depth is the main barrier to entry, for humans *and*
|
|
10
|
+
for LLMs.
|
|
11
|
+
|
|
12
|
+
**orbit-kit** wraps the common radial patterns into one-line custom elements —
|
|
13
|
+
gauges, charts, activity rings, knobs, pie menus, cockpit instruments, analog
|
|
14
|
+
clocks, compasses and radars:
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<!-- Before: raw primitives -->
|
|
18
|
+
<div class="bigbang" style="width:200px;aspect-ratio:1">
|
|
19
|
+
<div class="gravity-spot">
|
|
20
|
+
<div class="orbit-4" style="--o-range:270deg;--o-from:225deg">
|
|
21
|
+
<o-progress value="72" shape="rounded"
|
|
22
|
+
style="--o-fill:#6366f1;--o-stroke-width:8"></o-progress>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="orbit-0">
|
|
25
|
+
<div class="satellite at-center"><div class="capsule">72%</div></div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<!-- After: orbit-kit -->
|
|
31
|
+
<o-gauge value="72" unit="%"></o-gauge>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install @zumer/orbit @zumer/orbit-kit
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
import '@zumer/orbit/style' // Orbit CSS engine
|
|
44
|
+
import '@zumer/orbit' // Orbit core web components (<o-arc>, <o-progress>)
|
|
45
|
+
import '@zumer/orbit-kit' // registers every kit component
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Or straight from a CDN — no build step:
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<link rel="stylesheet" href="https://unpkg.com/@zumer/orbit@latest/dist/orbit.css">
|
|
52
|
+
<script src="https://unpkg.com/@zumer/orbit@latest/dist/orbit.js"></script>
|
|
53
|
+
<script type="module" src="https://unpkg.com/@zumer/orbit-kit@latest/src/orbit-kit.js"></script>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`@zumer/orbit` is a **peer dependency** — the kit assumes the Orbit core (CSS +
|
|
57
|
+
`<o-arc>` / `<o-progress>`) is present on the page.
|
|
58
|
+
|
|
59
|
+
Every component is a standard custom element, so it works in plain HTML and in
|
|
60
|
+
any framework (React, Vue, Svelte, Angular). Import the whole kit, or just the
|
|
61
|
+
components you use:
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
import '@zumer/orbit-kit/src/o-gauge.js'
|
|
65
|
+
import '@zumer/orbit-kit/src/o-clock.js'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Components
|
|
71
|
+
|
|
72
|
+
| Tag | What it is |
|
|
73
|
+
|-----|------------|
|
|
74
|
+
| [`<o-gauge>`](#o-gauge) | Single-value radial gauge |
|
|
75
|
+
| [`<o-donut>`](#o-donut) | Data-driven donut / pie chart |
|
|
76
|
+
| [`<o-rings>`](#o-rings) | Apple-style concentric activity rings |
|
|
77
|
+
| [`<o-knob>`](#o-knob) | Interactive rotary control (drag / wheel / keys) |
|
|
78
|
+
| [`<o-menu>`](#o-menu) | Radial / pie menu — single- & multi-level |
|
|
79
|
+
| [`<o-instrument>`](#o-instrument) | Cockpit / dashboard gauges (tach, speedo, fuel, airspeed…) |
|
|
80
|
+
| [`<o-clock>`](#o-clock) | Analog clock / watch face — the flagship |
|
|
81
|
+
| [`<o-compass>`](#o-compass) | Heading indicator |
|
|
82
|
+
| [`<o-radar>`](#o-radar) | Radar / scope display |
|
|
83
|
+
|
|
84
|
+
Common conventions across the kit:
|
|
85
|
+
|
|
86
|
+
- **`size`** sets the widget's pixel width (square aspect). Components scale
|
|
87
|
+
Orbit's `--o-force` internally so they actually fill that box.
|
|
88
|
+
- **`glow`** is an opt-in neon drop-shadow tinted with the indicator color
|
|
89
|
+
(`glow` or `glow="12"` for the blur radius).
|
|
90
|
+
- **`data` / `items` / `zones` / `targets`** take a JSON array in the attribute.
|
|
91
|
+
- Set attributes to update live (`el.setAttribute('value', 88)`); interactive
|
|
92
|
+
components also expose JS properties and events.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### `<o-gauge>`
|
|
97
|
+
|
|
98
|
+
A single value on an arc, with a center readout.
|
|
99
|
+
|
|
100
|
+
```html
|
|
101
|
+
<o-gauge value="72" unit="%"></o-gauge>
|
|
102
|
+
<o-gauge value="125" max="240" label="125 km/h" range="360" glow></o-gauge>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
| Attribute | Default | Description |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `value` | `0` | Current value. |
|
|
108
|
+
| `max` | `100` | Value that fills the whole arc. |
|
|
109
|
+
| `range` | `270` | Arc span in degrees (`360` = full ring, `180` = semicircle). |
|
|
110
|
+
| `from` | auto | Start angle; defaults so the gap sits at the bottom. |
|
|
111
|
+
| `size` | `200px` | Widget width. |
|
|
112
|
+
| `label` | — | Center text; overrides the auto `value`+`unit`. |
|
|
113
|
+
| `unit` | `""` | Suffix on the value (`%`, `km/h`). |
|
|
114
|
+
| `fill` | `#6366f1` | Progress color. |
|
|
115
|
+
| `track` | `#262d38` | Background ring color. |
|
|
116
|
+
| `stroke-width` | `8` | Ring thickness. |
|
|
117
|
+
| `shape` | `rounded` | Arc cap shape (`rounded`, `none`). |
|
|
118
|
+
| `glow` | off | Neon glow on the progress arc. |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### `<o-donut>`
|
|
123
|
+
|
|
124
|
+
A donut / pie chart from a JSON data array. Values are normalized to their sum,
|
|
125
|
+
or to an explicit `total` for a partial ring.
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
<o-donut label="$2.4k"
|
|
129
|
+
data='[{"value":52,"color":"#f97316","label":"Rent"},
|
|
130
|
+
{"value":28,"color":"#eab308"},
|
|
131
|
+
{"value":20,"color":"#22c55e"}]'></o-donut>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
| Attribute | Default | Description |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `data` | `[]` | `[{ value, color?, label? }]`. Colors fall back to a palette. |
|
|
137
|
+
| `total` | sum | Denominator; set it for a partial ring (e.g. `total="100"`). |
|
|
138
|
+
| `range` / `from` | `360` / `0` | Arc span and start angle. |
|
|
139
|
+
| `stroke-width` | `14` | Ring thickness. |
|
|
140
|
+
| `gap` | `2` | Spacing between segments. |
|
|
141
|
+
| `shape` | `none` | Segment caps (`none`, or `rounded` to opt in). |
|
|
142
|
+
| `track` | faint | Background ring color. |
|
|
143
|
+
| `label` / `unit` | — | Center text. |
|
|
144
|
+
| `glow` | off | Neon glow per segment. |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### `<o-rings>`
|
|
149
|
+
|
|
150
|
+
Apple-style concentric activity rings (outer ring first). Each ring gets a dim,
|
|
151
|
+
solid track in its own color.
|
|
152
|
+
|
|
153
|
+
```html
|
|
154
|
+
<o-rings data='[{"value":80,"color":"#fa114f"},
|
|
155
|
+
{"value":55,"color":"#a3ff00"},
|
|
156
|
+
{"value":92,"color":"#00fff0"}]'></o-rings>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
| Attribute | Default | Description |
|
|
160
|
+
|---|---|---|
|
|
161
|
+
| `data` | `[]` | `[{ value, max?, color?, label? }]`. |
|
|
162
|
+
| `range` / `from` | `360` | Arc span and start angle. |
|
|
163
|
+
| `stroke-width` | `12` | Ring thickness. |
|
|
164
|
+
| `step` | `1` | Orbit gap between rings. |
|
|
165
|
+
| `track` | auto | Track color (defaults to a dim wash of each ring's color). |
|
|
166
|
+
| `shape` | `rounded` | Cap shape. |
|
|
167
|
+
| `label` / `unit` | — | Center text. |
|
|
168
|
+
| `glow` | off | Neon glow per ring. |
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### `<o-knob>`
|
|
173
|
+
|
|
174
|
+
An interactive rotary control that behaves like a form input.
|
|
175
|
+
|
|
176
|
+
```html
|
|
177
|
+
<o-knob value="65" unit="%" fill="#3da9fc"></o-knob>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
knob.addEventListener('input', e => console.log(e.detail.value)); // while dragging
|
|
182
|
+
knob.addEventListener('change', e => console.log(e.detail.value)); // on release
|
|
183
|
+
knob.value = 40; // read / write
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Drag vertically, scroll the wheel, or use arrow keys / Page Up·Down / Home·End
|
|
187
|
+
when focused. `role="slider"` + ARIA values are set for you.
|
|
188
|
+
|
|
189
|
+
| Attribute | Default | Description |
|
|
190
|
+
|---|---|---|
|
|
191
|
+
| `value` | `min` | Current value. |
|
|
192
|
+
| `min` / `max` | `0` / `100` | Range. |
|
|
193
|
+
| `step` | `1` | Increment for keys / wheel and snapping. |
|
|
194
|
+
| `range` / `from` | `270` / auto | Arc span and start angle. |
|
|
195
|
+
| `size` | `160px` | Widget width. |
|
|
196
|
+
| `fill` | `#a1a1aa` | Value color. |
|
|
197
|
+
| `track` | `#27272a` | Track color. |
|
|
198
|
+
| `stroke-width` | `14` | Ring thickness. |
|
|
199
|
+
| `unit` / `label` | — | Center readout. |
|
|
200
|
+
| `disabled` | off | Non-interactive + dimmed. |
|
|
201
|
+
| `glow` | off | Neon glow on the value arc. |
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
### `<o-menu>`
|
|
206
|
+
|
|
207
|
+
A radial / pie menu. Items may nest via `children` for multi-level menus, with
|
|
208
|
+
two ways to reveal a branch.
|
|
209
|
+
|
|
210
|
+
```html
|
|
211
|
+
<o-menu label="EDIT" expand="concentric"
|
|
212
|
+
items='[
|
|
213
|
+
{"label":"Size","icon":"🔠","children":[
|
|
214
|
+
{"label":"Bigger","value":"size.up"},
|
|
215
|
+
{"label":"Smaller","value":"size.down"}]},
|
|
216
|
+
{"label":"Format","icon":"✏️","children":[
|
|
217
|
+
{"label":"Bold","value":"fmt.bold"},
|
|
218
|
+
{"label":"Italic","value":"fmt.italic"}]},
|
|
219
|
+
{"label":"Color","icon":"🎨","value":"color"}]'></o-menu>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
```js
|
|
223
|
+
menu.addEventListener('select', e => e.detail); // { value, label, item, path }
|
|
224
|
+
menu.addEventListener('navigate', e => e.detail); // { path, depth }
|
|
225
|
+
menu.back(); menu.reset(); menu.path; // navigation API
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
| Attribute | Default | Description |
|
|
229
|
+
|---|---|---|
|
|
230
|
+
| `items` | `[]` | `[{ label, value?, color?, icon?, children? }]` (recursive). |
|
|
231
|
+
| `expand` | `drill` | `drill` (ring replaces itself, center = back) or `concentric` (parent stays, children fan out — the editor look). |
|
|
232
|
+
| `theme` | `mono` | `mono` (slate sectors tinted by `accent` on hover/active) or `color` (refined per-item palette). |
|
|
233
|
+
| `accent` | `#3da9fc` | Accent for the `mono` theme. |
|
|
234
|
+
| `range` / `from` | `360` | Arc span and start angle (`270` for a quick action wheel). |
|
|
235
|
+
| `size` | `280px` | Widget width. |
|
|
236
|
+
| `gap` | `6` | Spacing between sectors. |
|
|
237
|
+
| `label` | `MENU` | Center / breadcrumb text. |
|
|
238
|
+
|
|
239
|
+
Keyboard: arrows move focus, Enter activates, Esc / Backspace goes back.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
### `<o-instrument>`
|
|
244
|
+
|
|
245
|
+
Cockpit / dashboard gauges with major + minor ticks, color zones, a redline, a
|
|
246
|
+
numeric scale, a needle and a digital readout. Driven by **presets**, or
|
|
247
|
+
configured by hand.
|
|
248
|
+
|
|
249
|
+
```html
|
|
250
|
+
<o-instrument preset="tachometer" value="6.2"></o-instrument>
|
|
251
|
+
<o-instrument preset="airspeed" value="120"></o-instrument>
|
|
252
|
+
<o-instrument min="0" max="100" value="40" unit="%" label="LOAD"
|
|
253
|
+
zones='[{"from":85,"to":100,"color":"#ef4444"}]'></o-instrument>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Presets:** `tachometer`, `speedometer`, `fuel`, `temp`, `boost`, `voltmeter`,
|
|
257
|
+
`airspeed`, `altimeter`, `cpu`, `gpu`, `vu`. Any attribute below overrides the
|
|
258
|
+
preset.
|
|
259
|
+
|
|
260
|
+
| Attribute | Default | Description |
|
|
261
|
+
|---|---|---|
|
|
262
|
+
| `value` | `min` | Current reading. |
|
|
263
|
+
| `min` / `max` | `0` / `100` | Scale range. |
|
|
264
|
+
| `range` / `from` | `270` / auto | Arc span and start angle. |
|
|
265
|
+
| `ticks` | `7` | Major tick / label count. |
|
|
266
|
+
| `minor` | `5` | Minor ticks between each pair of majors. |
|
|
267
|
+
| `zones` | `[]` | `[{ from, to, color }]` color bands (in value units). |
|
|
268
|
+
| `redline` | — | Sugar: a red zone from this value to `max`. |
|
|
269
|
+
| `tick-labels` | numeric | Custom scale labels, e.g. `'["E","½","F"]'`. |
|
|
270
|
+
| `unit` / `label` | — | Readout unit and caption. |
|
|
271
|
+
| `digital` | on | Show the numeric readout. |
|
|
272
|
+
| `case` | auto | `round`, `square`, `rounded`, or `bare` (auto for ≤180° arcs). |
|
|
273
|
+
| `accent` / `needle-color` / `tick-color` / `face` / `bezel` | — | Theming. |
|
|
274
|
+
| `size` | `240px` | Widget width. |
|
|
275
|
+
| `glow` | off | Neon glow on the needle / zones. |
|
|
276
|
+
|
|
277
|
+
> `<o-meter>` (a basic speedometer) still ships for back-compat but is
|
|
278
|
+
> superseded by `<o-instrument preset="speedometer">`.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
### `<o-clock>`
|
|
283
|
+
|
|
284
|
+
The flagship: an analog clock / watch face. Live or static, with case shapes,
|
|
285
|
+
an oval (tonneau) dial, numerals, smartwatch complications, chronograph
|
|
286
|
+
sub-dials, and an iOS activity-rings face.
|
|
287
|
+
|
|
288
|
+
```html
|
|
289
|
+
<o-clock live seconds></o-clock> <!-- ticking wall clock -->
|
|
290
|
+
<o-clock time="10:08:42" numerals="roman" case="square"></o-clock>
|
|
291
|
+
<o-clock preset="smartwatch" live brand="ORBIT"></o-clock>
|
|
292
|
+
<o-clock preset="chrono" live></o-clock> <!-- tri-compax sub-dials -->
|
|
293
|
+
<o-clock case="rectangle" dial="oval" live smooth></o-clock> <!-- tonneau -->
|
|
294
|
+
<o-clock dial="rings" live seconds></o-clock> <!-- iOS activity-ring clock -->
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Presets:** `classic`, `roman`, `minimal`, `chrono`, `smartwatch`.
|
|
298
|
+
|
|
299
|
+
| Attribute | Default | Description |
|
|
300
|
+
|---|---|---|
|
|
301
|
+
| `time` | now | Static `HH:MM[:SS]`. Omit for the current time. |
|
|
302
|
+
| `live` | off | Tick with the real clock. |
|
|
303
|
+
| `smooth` | off | Sweeping (not ticking) second hand; implies `seconds`. |
|
|
304
|
+
| `seconds` | off | Show a second hand. |
|
|
305
|
+
| `numerals` | `arabic` | `arabic`, `roman`, or `none`. |
|
|
306
|
+
| `case` | `round` | `round`, `square`, `rounded`, `rectangle`. |
|
|
307
|
+
| `dial` | `round` | `round`, `oval` (tonneau, matches the case), or `rings` (iOS face). |
|
|
308
|
+
| `digital` / `date` / `brand` | off / off / — | Complications. |
|
|
309
|
+
| `subdials` | off | Chronograph sub-dials (small-seconds / 24h / date). |
|
|
310
|
+
| `accent` / `hand-color` / `tick-color` / `face` / `bezel` / `case-bg` | — | Theming (also via CSS vars, e.g. `--ok-clock-accent`). |
|
|
311
|
+
| `size` | `300px` | Widget width. |
|
|
312
|
+
| `glow` | off | Neon glow on the hands. |
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
### `<o-compass>`
|
|
317
|
+
|
|
318
|
+
A heading indicator: a fixed dial with a top lubber index; the card (ticks +
|
|
319
|
+
cardinals) rotates so the current `heading` sits under the index.
|
|
320
|
+
|
|
321
|
+
```html
|
|
322
|
+
<o-compass heading="045"></o-compass>
|
|
323
|
+
<o-compass heading="120" labels="N,NE,E,SE,S,SW,W,NW"></o-compass>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
| Attribute | Default | Description |
|
|
327
|
+
|---|---|---|
|
|
328
|
+
| `heading` | `0` | Heading in degrees (clockwise from North). |
|
|
329
|
+
| `labels` | `N,E,S,W` | Cardinal labels (comma-separated). |
|
|
330
|
+
| `fill` | `#ef4444` | Accent (north tick + lubber). |
|
|
331
|
+
| `unit` | `°` | Readout suffix. |
|
|
332
|
+
| `size` | `220px` | Widget width. |
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
### `<o-radar>`
|
|
337
|
+
|
|
338
|
+
A radar / scope: concentric range rings, cross-hair spokes, a rotating
|
|
339
|
+
conic-gradient sweep, and glowing blips.
|
|
340
|
+
|
|
341
|
+
```html
|
|
342
|
+
<o-radar rings="4" spokes="12" label="SCAN"
|
|
343
|
+
targets='[{"angle":60,"dist":0.8},
|
|
344
|
+
{"angle":200,"dist":0.45},
|
|
345
|
+
{"angle":310,"dist":0.65,"color":"#ef4565"}]'></o-radar>
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
| Attribute | Default | Description |
|
|
349
|
+
|---|---|---|
|
|
350
|
+
| `targets` | `[]` | `[{ angle, dist, color? }]` — `angle` °CW from top, `dist` 0–1. |
|
|
351
|
+
| `rings` | `4` | Concentric range rings. |
|
|
352
|
+
| `spokes` | `8` | Radial cross-hair lines. |
|
|
353
|
+
| `sweep` | on | The rotating beam (`sweep="false"` to hide). |
|
|
354
|
+
| `sweep-width` | `110` | Beam span in degrees. |
|
|
355
|
+
| `speed` | `4` | Seconds per revolution. |
|
|
356
|
+
| `color` | `#22d3ee` | Scope color. |
|
|
357
|
+
| `label` | — | Center label. |
|
|
358
|
+
| `size` | `240px` | Widget width. |
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## How it works
|
|
363
|
+
|
|
364
|
+
orbit-kit components render their structure into the **light DOM** (not shadow
|
|
365
|
+
DOM) on purpose. Orbit positions elements through global CSS (`cos()` / `sin()`
|
|
366
|
+
/ `:has()`) and inherited `--o-*` custom properties, so a layout-composing
|
|
367
|
+
component must live in the light DOM for the engine and the cascade to reach it.
|
|
368
|
+
The generated markup is exactly the documented Orbit structure — "view source"
|
|
369
|
+
stays honest. Inspect any component in devtools to see the rings, satellites and
|
|
370
|
+
arcs it expands into.
|
|
371
|
+
|
|
372
|
+
Everything is theme-able through plain CSS custom properties; the convenience
|
|
373
|
+
attributes above just map onto them.
|
|
374
|
+
|
|
375
|
+
## Examples
|
|
376
|
+
|
|
377
|
+
Live showcase of every component: **[zumerlab.com/orbit-kit](https://zumerlab.com/orbit-kit)**
|
|
378
|
+
(source in [`docs/index.html`](docs/index.html)).
|
|
379
|
+
|
|
380
|
+
To run it locally, serve the repo over HTTP so the ES modules load:
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
npx serve .
|
|
384
|
+
# then open /docs/index.html
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## Browser support
|
|
388
|
+
|
|
389
|
+
Modern evergreen browsers. Orbit relies on `:has()` and CSS trigonometric
|
|
390
|
+
functions (`cos`, `sin`); the radar/clock visuals use `conic-gradient` and
|
|
391
|
+
`color-mix`. Orbit shows an upgrade notice on unsupported browsers.
|
|
392
|
+
|
|
393
|
+
## License
|
|
394
|
+
|
|
395
|
+
[MIT](LICENSE) — Juan Martin Muda · [Zumerlab](https://github.com/zumerlab)
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zumer/orbit-kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "High-level, declarative web components on top of the Orbit CSS framework — gauges, charts, activity rings, knobs, pie menus, cockpit instruments, analog clocks, compasses and radars, each in one tag.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/orbit-kit.js",
|
|
7
|
+
"module": "src/orbit-kit.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/orbit-kit.js",
|
|
10
|
+
"./*": "./src/*"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"src"
|
|
14
|
+
],
|
|
15
|
+
"sideEffects": [
|
|
16
|
+
"./src/*.js"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "node test/smoke.mjs",
|
|
20
|
+
"shots": "node test/shoot.mjs",
|
|
21
|
+
"shots:menus": "node test/shoot-menus.mjs"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@zumer/orbit": "^1.4.9"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"playwright": "^1.55.1"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"orbit",
|
|
31
|
+
"web-components",
|
|
32
|
+
"custom-elements",
|
|
33
|
+
"radial",
|
|
34
|
+
"gauge",
|
|
35
|
+
"donut-chart",
|
|
36
|
+
"activity-rings",
|
|
37
|
+
"knob",
|
|
38
|
+
"pie-menu",
|
|
39
|
+
"clock",
|
|
40
|
+
"compass",
|
|
41
|
+
"radar",
|
|
42
|
+
"dashboard",
|
|
43
|
+
"circular",
|
|
44
|
+
"ui",
|
|
45
|
+
"zumerlab"
|
|
46
|
+
],
|
|
47
|
+
"homepage": "https://zumerlab.com/orbit-kit",
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/zumerlab/orbit-kit.git"
|
|
51
|
+
},
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/zumerlab/orbit-kit/issues"
|
|
54
|
+
},
|
|
55
|
+
"author": "Juan Martin Muda - Zumerlab",
|
|
56
|
+
"license": "MIT"
|
|
57
|
+
}
|
package/src/base.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OrbitKitElement — shared base for orbit-kit high-level components.
|
|
3
|
+
*
|
|
4
|
+
* Every kit component collapses a multi-level Orbit recipe
|
|
5
|
+
* (.bigbang > .gravity-spot > .orbit-N > o-arc|o-progress|.satellite)
|
|
6
|
+
* into a single declarative tag. They all share the same lifecycle:
|
|
7
|
+
*
|
|
8
|
+
* readConfig() -> parse attributes into a plain config object
|
|
9
|
+
* structuralSignature(c) -> string; when it changes the subtree is rebuilt
|
|
10
|
+
* build(cfg) -> render the Orbit structure into the light DOM
|
|
11
|
+
* sync(cfg) -> patch cheap/animated values without a rebuild
|
|
12
|
+
*
|
|
13
|
+
* LIGHT DOM on purpose: Orbit positions elements through global CSS
|
|
14
|
+
* (cos()/sin()/:has()) and inherited --o-* custom properties, so the kit must
|
|
15
|
+
* render into the light DOM for orbit.css to apply and the inner <o-arc> /
|
|
16
|
+
* <o-progress> to participate in the cascade. Requires @zumer/orbit (CSS + JS)
|
|
17
|
+
* loaded on the page; orbit-kit declares it as a peer dependency.
|
|
18
|
+
*/
|
|
19
|
+
export class OrbitKitElement extends HTMLElement {
|
|
20
|
+
connectedCallback() {
|
|
21
|
+
this._render();
|
|
22
|
+
this.onConnected?.();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
attributeChangedCallback() {
|
|
26
|
+
if (this.isConnected) this._render();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
disconnectedCallback() {
|
|
30
|
+
this.onDisconnected?.();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Parse a numeric attribute with a fallback. */
|
|
34
|
+
_num(name, fallback) {
|
|
35
|
+
const raw = this.getAttribute(name);
|
|
36
|
+
if (raw === null || raw === '') return fallback;
|
|
37
|
+
const n = parseFloat(raw);
|
|
38
|
+
return Number.isFinite(n) ? n : fallback;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Read a string attribute with a fallback. */
|
|
42
|
+
_str(name, fallback) {
|
|
43
|
+
const raw = this.getAttribute(name);
|
|
44
|
+
return raw === null ? fallback : raw;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Parse a JSON attribute (array/object) with a fallback; never throws. */
|
|
48
|
+
_json(name, fallback) {
|
|
49
|
+
const raw = this.getAttribute(name);
|
|
50
|
+
if (raw === null || raw.trim() === '') return fallback;
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(raw);
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.warn(`<${this.localName}> invalid JSON in "${name}":`, err.message);
|
|
55
|
+
return fallback;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Default `from` angle that centers an arc's gap at the bottom (6 o'clock),
|
|
61
|
+
* matching the classic 3/4 gauge: range 270 -> from 225.
|
|
62
|
+
*/
|
|
63
|
+
_autoFrom(range) {
|
|
64
|
+
return ((360 - range / 2) % 360 + 360) % 360;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Opt-in neon glow. When the `glow` attribute is present, returns a
|
|
69
|
+
* `filter: drop-shadow(...)` fragment tinted with the indicator color so the
|
|
70
|
+
* fill reads as emissive. `glow="12"` overrides the blur radius (px).
|
|
71
|
+
*/
|
|
72
|
+
_glow(color, fallbackPx = 6) {
|
|
73
|
+
if (!this.hasAttribute('glow')) return '';
|
|
74
|
+
const px = this._num('glow', fallbackPx) || fallbackPx;
|
|
75
|
+
return `filter:drop-shadow(0 0 ${px}px ${color});`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
_render() {
|
|
79
|
+
const cfg = this.readConfig();
|
|
80
|
+
const sig = this.structuralSignature ? this.structuralSignature(cfg) : null;
|
|
81
|
+
if (!this._built || sig === null || sig !== this._sig) {
|
|
82
|
+
this.build(cfg);
|
|
83
|
+
this._sig = sig;
|
|
84
|
+
this._built = true;
|
|
85
|
+
}
|
|
86
|
+
this.sync?.(cfg);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Register a custom element once (safe against double-registration). */
|
|
91
|
+
export function define(tag, cls) {
|
|
92
|
+
if (typeof customElements !== 'undefined' && !customElements.get(tag)) {
|
|
93
|
+
customElements.define(tag, cls);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Inject a stylesheet once, keyed by id (used by animated components). */
|
|
98
|
+
export function injectStyleOnce(id, css) {
|
|
99
|
+
if (typeof document === 'undefined' || document.getElementById(id)) return;
|
|
100
|
+
const style = document.createElement('style');
|
|
101
|
+
style.id = id;
|
|
102
|
+
style.textContent = css;
|
|
103
|
+
document.head.appendChild(style);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Kit baseline overrides, injected once for the whole kit.
|
|
108
|
+
*
|
|
109
|
+
* Orbit's default theme draws `border: 1px solid currentcolor` on every
|
|
110
|
+
* `.satellite` (see orbit _themes.scss). For composed widgets that's an
|
|
111
|
+
* unwanted ring around the center label / cardinals / radar blips. We strip it
|
|
112
|
+
* — but ONLY inside kit components, so hand-written Orbit markup keeps the
|
|
113
|
+
* default. `:where()` keeps specificity at (0,1,0) and the rule lands after
|
|
114
|
+
* orbit.css in document order, so the user can still re-add a border.
|
|
115
|
+
*/
|
|
116
|
+
const KIT_BASE_CSS = `
|
|
117
|
+
:where(o-gauge,o-donut,o-rings,o-meter,o-knob,o-menu,o-compass,o-radar,o-clock,o-instrument) .satellite {
|
|
118
|
+
border: none;
|
|
119
|
+
background: transparent;
|
|
120
|
+
}
|
|
121
|
+
/* Shared center hub: a soft radial halo behind the label. Uses a gradient
|
|
122
|
+
(not a hard circle) so wide readouts like "125 km/h" never get clipped.
|
|
123
|
+
Theme it with --ok-hub-bg / --ok-hub-color. */
|
|
124
|
+
:where(o-gauge,o-donut,o-rings,o-meter,o-knob,o-compass) .ok-hub {
|
|
125
|
+
font-weight: 800;
|
|
126
|
+
text-align: center;
|
|
127
|
+
line-height: 1.15;
|
|
128
|
+
padding: .5em .7em;
|
|
129
|
+
color: var(--ok-hub-color, inherit);
|
|
130
|
+
background: radial-gradient(closest-side, var(--ok-hub-bg, rgba(8, 12, 18, .82)), transparent 100%);
|
|
131
|
+
}`;
|
|
132
|
+
|
|
133
|
+
injectStyleOnce('orbit-kit-base', KIT_BASE_CSS);
|