gaugeit.js 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/CONTRIBUTING.md +36 -0
- package/LICENSE +21 -0
- package/NOTICE.md +5 -0
- package/README.md +894 -0
- package/SECURITY.md +24 -0
- package/adapters/react/Gaugeit.d.ts +11 -0
- package/adapters/react/Gaugeit.js +66 -0
- package/adapters/react/Gaugeit.jsx +1 -0
- package/dist/gaugeit.cjs +4686 -0
- package/dist/gaugeit.cjs.map +7 -0
- package/dist/gaugeit.css +300 -0
- package/dist/gaugeit.d.ts +442 -0
- package/dist/gaugeit.esm.js +4683 -0
- package/dist/gaugeit.esm.js.map +7 -0
- package/dist/gaugeit.esm.min.js +2 -0
- package/dist/gaugeit.esm.min.js.map +7 -0
- package/dist/gaugeit.min.cjs +2 -0
- package/dist/gaugeit.min.cjs.map +7 -0
- package/dist/gaugeit.min.css +1 -0
- package/dist/gaugeit.standalone.js +4720 -0
- package/dist/gaugeit.standalone.js.map +7 -0
- package/dist/gaugeit.standalone.min.js +302 -0
- package/dist/gaugeit.standalone.min.js.map +7 -0
- package/dist/gaugeit.umd.js +4713 -0
- package/dist/gaugeit.umd.js.map +7 -0
- package/dist/gaugeit.umd.min.js +4 -0
- package/dist/gaugeit.umd.min.js.map +7 -0
- package/dist/manifest.json +24 -0
- package/docs/api.md +456 -0
- package/docs/architecture.md +224 -0
- package/docs/assets/arc_zones.png +0 -0
- package/docs/assets/center_zero.png +0 -0
- package/docs/assets/classic_instrument.png +0 -0
- package/docs/assets/classic_linear_center_zero.png +0 -0
- package/docs/assets/classic_linear_instrument.png +0 -0
- package/docs/assets/heritage_rolling_tape.png +0 -0
- package/docs/assets/heritage_round.png +0 -0
- package/docs/assets/line_scale.png +0 -0
- package/docs/assets/rose_balance.png +0 -0
- package/docs/creating-gauge-types.md +286 -0
- package/docs/development.md +56 -0
- package/docs/framework-integration.md +84 -0
- package/package.json +114 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Design goals
|
|
4
|
+
|
|
5
|
+
Gaugeit.js is designed around five constraints:
|
|
6
|
+
|
|
7
|
+
1. the runtime must remain dependency-free and small;
|
|
8
|
+
2. a gauge must work in server-rendered HTML without a framework;
|
|
9
|
+
3. rendering must be responsive and resolution-independent;
|
|
10
|
+
4. gauge types must be replaceable without changing animation or lifecycle code;
|
|
11
|
+
5. expensive work must be static whenever possible.
|
|
12
|
+
|
|
13
|
+
## Runtime layers
|
|
14
|
+
|
|
15
|
+
### Public API
|
|
16
|
+
|
|
17
|
+
`src/index.js` exposes the stable application API:
|
|
18
|
+
|
|
19
|
+
- instance creation and automatic mounting;
|
|
20
|
+
- gauge-type registration;
|
|
21
|
+
- the optional custom element;
|
|
22
|
+
- built-in layers and geometry helpers;
|
|
23
|
+
- the default type registry.
|
|
24
|
+
|
|
25
|
+
### Gauge core
|
|
26
|
+
|
|
27
|
+
`src/core/gauge.js` owns state and lifecycle:
|
|
28
|
+
|
|
29
|
+
- stable target value;
|
|
30
|
+
- currently displayed value;
|
|
31
|
+
- startup and update animation;
|
|
32
|
+
- optional visual jitter;
|
|
33
|
+
- pause, resume, refresh, option replacement, and destroy;
|
|
34
|
+
- page visibility and offscreen pausing with observer reconfiguration;
|
|
35
|
+
- accessibility attributes and DOM events.
|
|
36
|
+
|
|
37
|
+
The core does not know how an arc, face, scale, or pointer should look.
|
|
38
|
+
|
|
39
|
+
### Type registry
|
|
40
|
+
|
|
41
|
+
`GaugeTypeRegistry` maps a type name to:
|
|
42
|
+
|
|
43
|
+
- type-specific defaults;
|
|
44
|
+
- an ordered list of rendering layers;
|
|
45
|
+
- a human-readable description.
|
|
46
|
+
|
|
47
|
+
Built-in types are registered during module initialization. Applications may register additional types or replace a built-in type intentionally.
|
|
48
|
+
|
|
49
|
+
### Shared annunciator-light layer
|
|
50
|
+
|
|
51
|
+
`src/layers/light.js` is a reusable dynamic layer backed by the pure helpers in
|
|
52
|
+
`src/geometry/light.js`. Trigger evaluation is separated from rendering, while the
|
|
53
|
+
layer owns only SVG nodes and CSS state classes. This lets any built-in or custom type
|
|
54
|
+
insert the same component at its preferred paint position without duplicating timers,
|
|
55
|
+
threshold logic, gradients, or responsive calculations. Thresholds may read the stable
|
|
56
|
+
target, animated display, or jittered pointer value. Blinking is CSS-driven and respects
|
|
57
|
+
`prefers-reduced-motion`.
|
|
58
|
+
|
|
59
|
+
### Rendering layers
|
|
60
|
+
|
|
61
|
+
A layer receives a render context and adds SVG elements. Static layers return nothing. Dynamic layers return a controller:
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
{
|
|
65
|
+
update(value, state) {
|
|
66
|
+
// Update only the generated nodes owned by this layer.
|
|
67
|
+
},
|
|
68
|
+
destroy() {
|
|
69
|
+
// Optional cleanup.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The radial pointer, linear indicator, rolling tape, and readout are dynamic. Faces, zones, ticks, labels, and fixed reference markers are static until options change.
|
|
75
|
+
Layer-array order is also the SVG paint order. Built-in types place readouts before
|
|
76
|
+
their pointer or linear-indicator layer, so the moving visual stays above all text.
|
|
77
|
+
Center caps use the type-specific `cap.stackOrder` option to paint either above or
|
|
78
|
+
below their owning pointer/indicator without relying on unsupported numeric SVG
|
|
79
|
+
`z-index` behavior.
|
|
80
|
+
Controller `value` is always the rendered base value. The optional `state.pointerValue`
|
|
81
|
+
contains the pointer-only visual value after jitter and should be used only by pointer-like artwork.
|
|
82
|
+
|
|
83
|
+
### SVG renderer
|
|
84
|
+
|
|
85
|
+
`SvgRenderer` owns the generated subtree, dynamic-controller list, small reference registry, and the final content-aware viewport. It avoids `innerHTML`, creates all SVG nodes through `createElementNS`, and replaces only the content inside the supplied host.
|
|
86
|
+
|
|
87
|
+
Before the SVG is exposed, the renderer combines deterministic geometry bounds with a browser `getBBox()` measurement when available. Intrinsic layout grows with that viewport; contain layout scales the same viewport into a fixed host. This keeps all layers inside the component without coupling gauge types to page-specific card dimensions.
|
|
88
|
+
|
|
89
|
+
SVG provides:
|
|
90
|
+
|
|
91
|
+
- one coordinate system through `viewBox`;
|
|
92
|
+
- crisp rendering at any pixel density;
|
|
93
|
+
- CSS and custom-property compatibility;
|
|
94
|
+
- accessible normal DOM nodes;
|
|
95
|
+
- cheap pointer rotation through one group transform.
|
|
96
|
+
|
|
97
|
+
### Geometry
|
|
98
|
+
|
|
99
|
+
Pure geometry helpers map values to radial angles, two-point linear axes, and rolling-tape displacement. They also calculate points, ticks, zones, closed-scale endpoints, tape windows, and visual bounds. The helpers are DOM-independent and covered by Node tests.
|
|
100
|
+
|
|
101
|
+
Transparent-zone handling is performed by splitting the full range at every zone boundary. Each resulting interval is assigned either a zone, the base track, or no visual stroke. This ensures that a transparent zone is a real gap rather than a transparent stroke over a visible track.
|
|
102
|
+
|
|
103
|
+
The radial zone layer selects one of two renderers per resolved interval. Uniform zones
|
|
104
|
+
remain compact SVG strokes. A zone with `taper` becomes a sampled, filled annular path
|
|
105
|
+
whose inner and outer boundaries interpolate independently from `startWidth` to
|
|
106
|
+
`endWidth`. The bounds system reserves the widest endpoint, and overlapping-zone splits
|
|
107
|
+
preserve the taper ratio across the original numeric zone instead of restarting it.
|
|
108
|
+
|
|
109
|
+
### Radial and linear layer families
|
|
110
|
+
|
|
111
|
+
Radial types compose `zones`, `ticks`, `labels`, and `pointer`. Linear types compose
|
|
112
|
+
`linearZones`, `linearTicks`, `linearLabels`, and `linearIndicator`. Both families use
|
|
113
|
+
the same face, readout, type registry, animation state, zone semantics, option merge,
|
|
114
|
+
accessibility, and renderer. This prevents linear geometry from being forced through
|
|
115
|
+
radial angle helpers while keeping the lifecycle and public API shared.
|
|
116
|
+
|
|
117
|
+
The built-in `classic-linear` type is therefore a preset, not a separate rendering
|
|
118
|
+
engine. Its straight axis is configured by start and end points, which also permits
|
|
119
|
+
vertical or diagonal custom types without changing the core. The shared indicator
|
|
120
|
+
model uses `centerOffset` for an orientation-independent positive-up control and keeps
|
|
121
|
+
the older raw axis-normal `offset` as a compatibility alias. Public linear Y coordinates
|
|
122
|
+
are Cartesian around `linear.originY`: positive values move upward even though SVG's
|
|
123
|
+
internal Y axis points downward. Labels and zones use the same positive-up convention.
|
|
124
|
+
|
|
125
|
+
Framed presets register an SVG clip path that matches the inner bezel. The renderer
|
|
126
|
+
applies it to every later non-face layer, so custom pointer or indicator geometry can
|
|
127
|
+
enter from outside the face without affecting document layout or leaking past the
|
|
128
|
+
instrument frame. Content-fitted faces still size themselves from conservative
|
|
129
|
+
unclipped bounds before the clip is applied.
|
|
130
|
+
|
|
131
|
+
### Specialized gauge families
|
|
132
|
+
|
|
133
|
+
Gaugeit keeps one lifecycle and registry while allowing specialized geometry families:
|
|
134
|
+
|
|
135
|
+
- `center-zero` remains radial and reuses the ordinary zones, ticks, labels, pointer,
|
|
136
|
+
and readout layers. Its only additional layer is a fixed configurable zero marker.
|
|
137
|
+
- `classic-linear-zero` reuses the complete classic linear family and inserts the same
|
|
138
|
+
zero-marker layer between the scale and readout. The marker resolves its geometry
|
|
139
|
+
from the active axis rather than duplicating a linear renderer.
|
|
140
|
+
- `rolling-tape` uses a clipped, bounded set of recyclable tape slots. A fixed
|
|
141
|
+
edge-to-edge reference line stays in place while labels and ticks move behind it.
|
|
142
|
+
Slot reuse keeps DOM size independent of the configured numeric range.
|
|
143
|
+
|
|
144
|
+
These families expose normal option objects (`centerZero` and `tape`) and
|
|
145
|
+
ordinary color properties. Type presets provide polished fallback themes, while an
|
|
146
|
+
instance may override every face, scale, label, fill, strip, indicator, fade, and zone
|
|
147
|
+
color through the same recursive option merge used by existing gauges.
|
|
148
|
+
|
|
149
|
+
## Configuration resolution
|
|
150
|
+
|
|
151
|
+
Configuration uses three ordered sources:
|
|
152
|
+
|
|
153
|
+
1. base library defaults;
|
|
154
|
+
2. selected gauge-type defaults;
|
|
155
|
+
3. instance options.
|
|
156
|
+
|
|
157
|
+
Plain objects are merged recursively. Arrays are replaced. Functions are preserved.
|
|
158
|
+
`updateOptions()` merges a partial patch; `replaceOptions()` discards earlier instance
|
|
159
|
+
overrides and resolves a complete declarative option object from fresh type defaults.
|
|
160
|
+
A type may optionally expose `configure(options, context)` after merging and before
|
|
161
|
+
validation. The built-in resizable linear and tape presets use this hook to scale only
|
|
162
|
+
their fallback coordinates from `geometry.width`, `geometry.height`, or
|
|
163
|
+
`geometry.aspectRatio`; explicit instance coordinates are never overwritten. The result
|
|
164
|
+
is then normalized and validated before rendering.
|
|
165
|
+
|
|
166
|
+
This allows a type to define nearly every visual choice while an application supplies only `value`, `min`, and `max`.
|
|
167
|
+
|
|
168
|
+
## Animation model
|
|
169
|
+
|
|
170
|
+
A stable base value is animated between the previous and requested values. Jitter is an optional visual offset supplied only to the pointer controller after base animation. Consequently:
|
|
171
|
+
|
|
172
|
+
- `getValue()` returns the stable requested value;
|
|
173
|
+
- `getDisplayedValue()` returns the rendered base value and may include animation, but never jitter;
|
|
174
|
+
- changing jitter never mutates application data;
|
|
175
|
+
- readout, events, and ARIA values do not jitter;
|
|
176
|
+
- pointer-like visuals and the rolling tape read their visual value from `state.pointerValue`.
|
|
177
|
+
|
|
178
|
+
One `requestAnimationFrame` loop per active gauge is used. The loop stops when no
|
|
179
|
+
animation or jitter remains. Visibility and intersection observers prevent unnecessary
|
|
180
|
+
background work and are reconfigured when performance options change.
|
|
181
|
+
|
|
182
|
+
## Events
|
|
183
|
+
|
|
184
|
+
Instances dispatch namespaced DOM events from the host:
|
|
185
|
+
|
|
186
|
+
- `gaugeit:render`;
|
|
187
|
+
- `gaugeit:change`;
|
|
188
|
+
- `gaugeit:displaychange`;
|
|
189
|
+
- `gaugeit:animationstart`;
|
|
190
|
+
- `gaugeit:animationend`;
|
|
191
|
+
- `gaugeit:settled`;
|
|
192
|
+
- `gaugeit:typechange`;
|
|
193
|
+
- `gaugeit:pause`;
|
|
194
|
+
- `gaugeit:resume`;
|
|
195
|
+
- `gaugeit:destroy`.
|
|
196
|
+
|
|
197
|
+
Events enable framework adapters and independent components without unscoped global
|
|
198
|
+
callbacks. Managed instances also register an internal destroy callback, so calling
|
|
199
|
+
`gauge.destroy()` directly removes the stale entry used by `getMountedGauge()` and
|
|
200
|
+
duplicate-safe auto mounting.
|
|
201
|
+
|
|
202
|
+
## Distribution model
|
|
203
|
+
|
|
204
|
+
The source remains normal ES modules. A dependency-free build script creates:
|
|
205
|
+
|
|
206
|
+
- ESM bundle for modern bundlers and direct module use;
|
|
207
|
+
- UMD bundle for classic script tags and AMD;
|
|
208
|
+
- CommonJS bundle for tooling;
|
|
209
|
+
- compact UMD build;
|
|
210
|
+
- normal and compact CSS;
|
|
211
|
+
- TypeScript declarations for the core and React adapter;
|
|
212
|
+
- a file-size manifest.
|
|
213
|
+
|
|
214
|
+
The prebuilt UMD package exposes one `Gaugeit` global. Internal modules do not leak global names.
|
|
215
|
+
|
|
216
|
+
## Framework boundaries
|
|
217
|
+
|
|
218
|
+
The core never imports React, Laravel, Twig, Bootstrap, jQuery, or a transport library. Framework adapters own only lifecycle translation:
|
|
219
|
+
|
|
220
|
+
- React creates and destroys an instance from effects;
|
|
221
|
+
- Blade or Twig emits a stable host and data configuration;
|
|
222
|
+
- the custom element translates attributes into normal options.
|
|
223
|
+
|
|
224
|
+
This boundary keeps the rendering engine portable and testable.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Creating gauge types and layers
|
|
2
|
+
|
|
3
|
+
## Reusing built-in layers
|
|
4
|
+
|
|
5
|
+
The easiest type changes defaults while reusing all standard layers:
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
Gaugeit.registerGaugeType('compact-status', {
|
|
9
|
+
description: 'Compact status gauge for dashboard cards.',
|
|
10
|
+
defaults: {
|
|
11
|
+
geometry: {
|
|
12
|
+
width: 260,
|
|
13
|
+
height: 150,
|
|
14
|
+
centerX: 130,
|
|
15
|
+
centerY: 132,
|
|
16
|
+
startAngle: 200,
|
|
17
|
+
endAngle: 340
|
|
18
|
+
},
|
|
19
|
+
pointer: {
|
|
20
|
+
type: 'arrow',
|
|
21
|
+
length: 75,
|
|
22
|
+
width: 6
|
|
23
|
+
},
|
|
24
|
+
readout: {
|
|
25
|
+
value: { x: 130, y: 35, fontSize: 28 }
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
layers: [
|
|
29
|
+
Gaugeit.layers.face,
|
|
30
|
+
Gaugeit.layers.zones,
|
|
31
|
+
Gaugeit.layers.ticks,
|
|
32
|
+
Gaugeit.layers.labels,
|
|
33
|
+
Gaugeit.layers.pointer,
|
|
34
|
+
Gaugeit.layers.readout
|
|
35
|
+
]
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Layer order is paint order. Later SVG layers appear above earlier layers.
|
|
40
|
+
|
|
41
|
+
For a standard warning or status lamp, include `Gaugeit.layers.light` and provide a
|
|
42
|
+
normal `light` option block. The shared component already supplies threshold/range
|
|
43
|
+
conditions, realistic palettes, blinking, custom colors, and responsive positioning.
|
|
44
|
+
A custom lamp layer is only necessary when the visual itself is fundamentally different.
|
|
45
|
+
|
|
46
|
+
Any custom radial type that includes `Gaugeit.layers.zones` automatically supports both
|
|
47
|
+
uniform and tapered sectors. A tapered zone uses the same public zone object:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
zones: [{
|
|
51
|
+
min: 0,
|
|
52
|
+
max: 100,
|
|
53
|
+
color: '#06b6d4',
|
|
54
|
+
width: 18,
|
|
55
|
+
taper: { startWidth: 0, endWidth: 36, segments: 48 }
|
|
56
|
+
}]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
No type-specific layout code is required; automatic bounds use the widest taper endpoint.
|
|
60
|
+
|
|
61
|
+
## Reusing the linear layer family
|
|
62
|
+
|
|
63
|
+
A linear type uses the same lifecycle and option system without routing straight
|
|
64
|
+
geometry through radial angle helpers:
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
Gaugeit.registerGaugeType('vertical-level', {
|
|
68
|
+
description: 'Vertical classic level instrument.',
|
|
69
|
+
defaults: {
|
|
70
|
+
geometry: { mode: 'linear', width: 180, height: 320 },
|
|
71
|
+
linear: {
|
|
72
|
+
originY: 270,
|
|
73
|
+
startX: 90,
|
|
74
|
+
startY: 0,
|
|
75
|
+
endX: 90,
|
|
76
|
+
endY: 220,
|
|
77
|
+
tickSide: 'positive',
|
|
78
|
+
labelOffset: 34,
|
|
79
|
+
zoneOffset: -16
|
|
80
|
+
},
|
|
81
|
+
pointer: { visible: false },
|
|
82
|
+
indicator: {
|
|
83
|
+
visible: true,
|
|
84
|
+
type: 'hairline',
|
|
85
|
+
position: 'cross',
|
|
86
|
+
length: 70,
|
|
87
|
+
centerOffset: 0
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
layers: [
|
|
91
|
+
Gaugeit.layers.face,
|
|
92
|
+
Gaugeit.layers.linearZones,
|
|
93
|
+
Gaugeit.layers.linearTicks,
|
|
94
|
+
Gaugeit.layers.linearLabels,
|
|
95
|
+
Gaugeit.layers.linearIndicator,
|
|
96
|
+
Gaugeit.layers.readout
|
|
97
|
+
]
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The axis start and end points define the complete orientation. X increases rightward;
|
|
102
|
+
Y is measured from `linear.originY` and increases upward. The linear indicator reads
|
|
103
|
+
`state.pointerValue`, so animation and jitter work without a separate lifecycle.
|
|
104
|
+
Use `indicator.centerOffset` to move the indicator center across the axis; positive
|
|
105
|
+
values move toward the visual upper side of a left-to-right horizontal axis. Tick
|
|
106
|
+
lengths are shared with radial types through `scale.major.length` and
|
|
107
|
+
`scale.minor.length`.
|
|
108
|
+
|
|
109
|
+
A framed custom type can set `face.clipContent: true` when the face layer is first in
|
|
110
|
+
paint order. The face layer then defines the inner-bezel clip path and the renderer
|
|
111
|
+
applies it to subsequent layers automatically.
|
|
112
|
+
|
|
113
|
+
## A custom static layer
|
|
114
|
+
|
|
115
|
+
A static layer may use normal SVG DOM methods:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
const targetMarkerLayer = {
|
|
119
|
+
id: 'target-marker',
|
|
120
|
+
render({ options, renderer }) {
|
|
121
|
+
const group = renderer.group('target-marker', { 'aria-hidden': 'true' });
|
|
122
|
+
const angle = Gaugeit.geometry.valueToAngle(options.targetMarker.value, options);
|
|
123
|
+
const inner = Gaugeit.geometry.polarPoint(
|
|
124
|
+
options.geometry.centerX,
|
|
125
|
+
options.geometry.centerY,
|
|
126
|
+
options.targetMarker.radius - options.targetMarker.length,
|
|
127
|
+
angle
|
|
128
|
+
);
|
|
129
|
+
const outer = Gaugeit.geometry.polarPoint(
|
|
130
|
+
options.geometry.centerX,
|
|
131
|
+
options.geometry.centerY,
|
|
132
|
+
options.targetMarker.radius,
|
|
133
|
+
angle
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
137
|
+
line.setAttribute('x1', inner.x);
|
|
138
|
+
line.setAttribute('y1', inner.y);
|
|
139
|
+
line.setAttribute('x2', outer.x);
|
|
140
|
+
line.setAttribute('y2', outer.y);
|
|
141
|
+
line.setAttribute('stroke', options.targetMarker.color);
|
|
142
|
+
line.setAttribute('stroke-width', options.targetMarker.width);
|
|
143
|
+
group.append(line);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## A dynamic custom layer
|
|
149
|
+
|
|
150
|
+
A layer may return a controller. The following indicator changes opacity when the displayed value crosses a threshold:
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
const warningLampLayer = {
|
|
154
|
+
id: 'warning-lamp',
|
|
155
|
+
render({ options, renderer }) {
|
|
156
|
+
const group = renderer.group('warning-lamp');
|
|
157
|
+
const lamp = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
158
|
+
lamp.setAttribute('cx', options.warningLamp.x);
|
|
159
|
+
lamp.setAttribute('cy', options.warningLamp.y);
|
|
160
|
+
lamp.setAttribute('r', options.warningLamp.radius);
|
|
161
|
+
lamp.setAttribute('fill', options.warningLamp.color);
|
|
162
|
+
group.append(lamp);
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
update(value) {
|
|
166
|
+
lamp.setAttribute('opacity', value >= options.warningLamp.threshold ? '1' : '0.16');
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The controller's `value` argument excludes jitter, so readouts, lamps, and other
|
|
174
|
+
meaningful indicators remain stable. A custom pointer that intentionally participates
|
|
175
|
+
in the visual effect can read `state.pointerValue`:
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
update(value, state) {
|
|
179
|
+
const pointerValue = Number.isFinite(state.pointerValue) ? state.pointerValue : value;
|
|
180
|
+
// Rotate only the custom pointer using pointerValue.
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Register it with type defaults:
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
Gaugeit.registerGaugeType('warning-meter', {
|
|
188
|
+
defaults: {
|
|
189
|
+
warningLamp: {
|
|
190
|
+
x: 285,
|
|
191
|
+
y: 25,
|
|
192
|
+
radius: 8,
|
|
193
|
+
threshold: 80,
|
|
194
|
+
color: '#dc2626'
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
layers: [
|
|
198
|
+
Gaugeit.layers.face,
|
|
199
|
+
Gaugeit.layers.zones,
|
|
200
|
+
Gaugeit.layers.ticks,
|
|
201
|
+
Gaugeit.layers.labels,
|
|
202
|
+
warningLampLayer,
|
|
203
|
+
Gaugeit.layers.pointer,
|
|
204
|
+
Gaugeit.layers.readout
|
|
205
|
+
]
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Layer rules
|
|
210
|
+
|
|
211
|
+
A well-behaved layer should:
|
|
212
|
+
|
|
213
|
+
- append content only below a group created through `renderer.group()`;
|
|
214
|
+
- avoid changing application-owned nodes outside the generated gauge root;
|
|
215
|
+
- keep static geometry out of `update()`;
|
|
216
|
+
- release timers or observers through an optional controller `destroy()` method;
|
|
217
|
+
- treat `options` as read-only runtime configuration;
|
|
218
|
+
- avoid global event listeners unless ownership and cleanup are explicit;
|
|
219
|
+
- include a textual alternative when it introduces new meaning not represented by the gauge value.
|
|
220
|
+
|
|
221
|
+
Layer order is SVG paint order: later layers appear above earlier layers. Place a
|
|
222
|
+
pointer layer after a readout layer when the pointer must cross over the text. The
|
|
223
|
+
built-in radial types do this explicitly. Pointer and linear-indicator caps also
|
|
224
|
+
support `cap.stackOrder: 'above' | 'below'`, allowing each gauge type to choose the
|
|
225
|
+
cap-to-pointer relationship independently.
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
### Optional type configuration hook
|
|
229
|
+
|
|
230
|
+
A type definition may provide `configure(options, context)`. It runs after base, type,
|
|
231
|
+
and instance options have been merged but before validation. `context.requested` contains
|
|
232
|
+
the raw instance options and `context.typeDefaults` contains the preset defaults. This is
|
|
233
|
+
the correct place to derive fallback geometry from a requested aspect ratio or size.
|
|
234
|
+
Never replace coordinates explicitly supplied by the instance.
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
registerGaugeType('wide-linear', {
|
|
238
|
+
defaults,
|
|
239
|
+
configure(options, { requested, typeDefaults }) {
|
|
240
|
+
// Derive only unspecified fallback coordinates here.
|
|
241
|
+
},
|
|
242
|
+
layers
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Completely different gauge shapes
|
|
247
|
+
|
|
248
|
+
A type may ignore every built-in geometry family and render its own SVG composition.
|
|
249
|
+
Keep application data in options, create generated nodes only through the renderer,
|
|
250
|
+
and return a controller only for parts that change with the value. The core still
|
|
251
|
+
provides option resolution, animation state, events, accessibility updates, lifecycle
|
|
252
|
+
cleanup, and framework integration.
|
|
253
|
+
|
|
254
|
+
## Reusing the specialized built-in families
|
|
255
|
+
|
|
256
|
+
The public `Gaugeit.layers` and `Gaugeit.geometry` collections also expose the
|
|
257
|
+
specialized pieces used by the advanced built-ins:
|
|
258
|
+
|
|
259
|
+
- `Gaugeit.layers.zeroMarker` for a fixed radial or linear reference mark;
|
|
260
|
+
- `Gaugeit.layers.linearZones`, `linearTicks`, `linearLabels`, and `linearIndicator`
|
|
261
|
+
for straight-axis instruments;
|
|
262
|
+
- `Gaugeit.layers.rollingTape` for fixed-line moving-tape instruments;
|
|
263
|
+
- `Gaugeit.layers.light` for reusable warning and status lamps;
|
|
264
|
+
- linear helpers such as `linearAxis()` and `valueToLinearPoint()`;
|
|
265
|
+
- tape helpers such as `tapeWindowBox()`, `resolveTapeInterval()`, and
|
|
266
|
+
`tapeValueOffset()`.
|
|
267
|
+
|
|
268
|
+
A custom type can therefore reuse a complete geometry family while changing only
|
|
269
|
+
preset defaults and layer order. Keep family-specific options below a namespaced
|
|
270
|
+
object such as `tape`, `centerZero`, or a new plugin-owned key. Colors belong in options,
|
|
271
|
+
not hard-coded CSS, so every instance can override the preset theme.
|
|
272
|
+
|
|
273
|
+
For moving artwork, use `state.pointerValue` only when it should participate in the
|
|
274
|
+
visual jitter effect. Use `state.targetValue` for stable application intent and the
|
|
275
|
+
controller's base `value` for animated display semantics. The shared light layer exposes
|
|
276
|
+
this choice through `light.trigger.source`.
|
|
277
|
+
|
|
278
|
+
A type does not need to use any built-in layer. It can render a vertical meter, dual pointer instrument, compass, clock, or bar-style visualization while retaining:
|
|
279
|
+
|
|
280
|
+
- instance lifecycle;
|
|
281
|
+
- option resolution;
|
|
282
|
+
- value animation;
|
|
283
|
+
- jitter;
|
|
284
|
+
- events;
|
|
285
|
+
- accessibility updates;
|
|
286
|
+
- framework adapters.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Development and release process
|
|
2
|
+
|
|
3
|
+
Gaugeit.js `0.1.0` is the first release line. The repository is structured so the same
|
|
4
|
+
canonical sources produce every npm, Composer, GitHub, and CDN-facing artifact.
|
|
5
|
+
Publishing remains an explicit maintainer action; CI validates but does not publish.
|
|
6
|
+
|
|
7
|
+
## Source of truth
|
|
8
|
+
|
|
9
|
+
Maintain JavaScript and canonical CSS in `src/`. Maintain the React adapter, PHP asset
|
|
10
|
+
installer, tests, documentation, and build configuration in their respective source
|
|
11
|
+
directories. Never edit generated files in `dist/`, including minified files, source
|
|
12
|
+
maps, copied declarations, or standalone embedded CSS.
|
|
13
|
+
|
|
14
|
+
`src/gaugeit.css` is the only maintained stylesheet. Both separate CSS files and the
|
|
15
|
+
standalone JavaScript bundles are generated from it.
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm ci
|
|
21
|
+
npm run clean
|
|
22
|
+
npm run build
|
|
23
|
+
npm run typecheck
|
|
24
|
+
npm test
|
|
25
|
+
npm run test:browser
|
|
26
|
+
npm run check
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`npm run dev` starts the esbuild watcher. `npm run check` performs a clean rebuild,
|
|
30
|
+
validates declarations, runs unit and DOM smoke tests, checks npm package contents with
|
|
31
|
+
a dry run, and runs strict Composer validation when Composer is available.
|
|
32
|
+
|
|
33
|
+
The optional `npm run test:chromium` check requires a globally available Chromium
|
|
34
|
+
executable and is separate from portable CI.
|
|
35
|
+
|
|
36
|
+
## Why `dist/` is committed
|
|
37
|
+
|
|
38
|
+
The complete `dist/` directory is intentionally committed because browser users,
|
|
39
|
+
GitHub source downloads, Composer consumers, npm packages, and CDN endpoints require
|
|
40
|
+
ready-to-use files. Consumers do not need Node.js or a build step. CI rebuilds `dist/`
|
|
41
|
+
from canonical sources and verifies that committed output remains reproducible.
|
|
42
|
+
|
|
43
|
+
## Release checklist
|
|
44
|
+
|
|
45
|
+
1. Confirm the version in `package.json`, `src/index.js`, `CHANGELOG.md`, and generated
|
|
46
|
+
`dist/manifest.json`.
|
|
47
|
+
2. Run `npm ci` and `npm run check`.
|
|
48
|
+
3. Run `npm pack --dry-run` and inspect the included files.
|
|
49
|
+
4. Run `npm publish --dry-run`.
|
|
50
|
+
5. Run `composer validate --strict`.
|
|
51
|
+
6. Test the npm archive and Composer package from isolated consumer projects.
|
|
52
|
+
7. Confirm that regenerated `dist/` files and declarations are committed.
|
|
53
|
+
8. Review the working tree for local exports, patches, credentials, and editor files.
|
|
54
|
+
9. Create the Git tag and GitHub release, publish npm, and update Packagist manually.
|
|
55
|
+
|
|
56
|
+
No automatic publishing workflow or credential-dependent release step is included.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Framework integration
|
|
2
|
+
|
|
3
|
+
## Plain HTML
|
|
4
|
+
|
|
5
|
+
Use `dist/gaugeit.umd.min.js` and `dist/gaugeit.min.css`. The UMD build creates one
|
|
6
|
+
`Gaugeit` global.
|
|
7
|
+
|
|
8
|
+
## Native ES modules
|
|
9
|
+
|
|
10
|
+
Use `dist/gaugeit.esm.js`. Import CSS separately or copy it into the application
|
|
11
|
+
stylesheet.
|
|
12
|
+
|
|
13
|
+
## React
|
|
14
|
+
|
|
15
|
+
Use `adapters/react/Gaugeit.js` or the package export `gaugeit.js/react`. Import
|
|
16
|
+
`gaugeit.js/css` once in the application entry point. The published adapter is ordinary
|
|
17
|
+
ESM JavaScript, so the consuming toolchain does not need to compile JSX from
|
|
18
|
+
`node_modules`. Keep the options object stable with `useMemo` in frequently rendering
|
|
19
|
+
components. The adapter replaces the complete options object when it changes, keeps an
|
|
20
|
+
explicit `value` prop authoritative, forwards a ref to the live `Gauge` instance, and
|
|
21
|
+
destroys the instance on unmount.
|
|
22
|
+
|
|
23
|
+
## Laravel Blade
|
|
24
|
+
|
|
25
|
+
Install the recommended prebuilt assets:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
vendor/bin/gaugeit-install-assets public/assets/vendor/gaugeit/0.1.0
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The default command installs `gaugeit.umd.min.js` and `gaugeit.min.css`. Pass
|
|
32
|
+
`--standalone` for the one-file browser build or `--all` for every browser asset.
|
|
33
|
+
Composer consumers use committed prebuilt files and do not need Node.js.
|
|
34
|
+
|
|
35
|
+
Recommended application structure:
|
|
36
|
+
|
|
37
|
+
1. install assets into a versioned public vendor path;
|
|
38
|
+
2. render a stable host and JSON configuration from Blade;
|
|
39
|
+
3. include the vendor CSS and script once;
|
|
40
|
+
4. initialize matching hosts with a duplicate-safe selector;
|
|
41
|
+
5. keep surrounding layout in application CSS.
|
|
42
|
+
|
|
43
|
+
See `examples/laravel-blade.blade.php` for an executable UMD-based example. It uses
|
|
44
|
+
JSON hex-escaping for safe HTML attributes, Blade `@once` blocks to avoid duplicate
|
|
45
|
+
asset tags, and a `DOMContentLoaded` initializer so every server-rendered occurrence is
|
|
46
|
+
available before the single initialization pass runs. The initializer is also exposed
|
|
47
|
+
as `window.initGaugeitStatGauges(root)` for fragments inserted later.
|
|
48
|
+
|
|
49
|
+
## Twig and other server-rendered templates
|
|
50
|
+
|
|
51
|
+
A reusable initializer can keep instances in a `WeakMap`:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
import { createGauge } from './vendor/gaugeit/0.1.0/gaugeit.esm.js';
|
|
55
|
+
|
|
56
|
+
const instances = new WeakMap();
|
|
57
|
+
|
|
58
|
+
export function initStatGauges(root = document) {
|
|
59
|
+
root.querySelectorAll('[data-stat-gauge]').forEach((element) => {
|
|
60
|
+
if (instances.has(element)) return;
|
|
61
|
+
|
|
62
|
+
const options = JSON.parse(element.dataset.statGaugeOptions || '{}');
|
|
63
|
+
const gauge = createGauge(element, options);
|
|
64
|
+
instances.set(element, gauge);
|
|
65
|
+
|
|
66
|
+
element.addEventListener('gaugeit:destroy', () => {
|
|
67
|
+
instances.delete(element);
|
|
68
|
+
}, { once: true });
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Vue, Svelte, and other frameworks
|
|
74
|
+
|
|
75
|
+
The lifecycle pattern is the same:
|
|
76
|
+
|
|
77
|
+
- create the gauge after the host mounts;
|
|
78
|
+
- call `setValue` for value-only changes;
|
|
79
|
+
- call `updateOptions` for partial visual changes;
|
|
80
|
+
- call `replaceOptions` when a declarative options object should replace earlier
|
|
81
|
+
overrides;
|
|
82
|
+
- call `destroy` before the host unmounts.
|
|
83
|
+
|
|
84
|
+
No framework-specific rendering assumptions exist in the core.
|