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.
Files changed (43) hide show
  1. package/CONTRIBUTING.md +36 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE.md +5 -0
  4. package/README.md +894 -0
  5. package/SECURITY.md +24 -0
  6. package/adapters/react/Gaugeit.d.ts +11 -0
  7. package/adapters/react/Gaugeit.js +66 -0
  8. package/adapters/react/Gaugeit.jsx +1 -0
  9. package/dist/gaugeit.cjs +4686 -0
  10. package/dist/gaugeit.cjs.map +7 -0
  11. package/dist/gaugeit.css +300 -0
  12. package/dist/gaugeit.d.ts +442 -0
  13. package/dist/gaugeit.esm.js +4683 -0
  14. package/dist/gaugeit.esm.js.map +7 -0
  15. package/dist/gaugeit.esm.min.js +2 -0
  16. package/dist/gaugeit.esm.min.js.map +7 -0
  17. package/dist/gaugeit.min.cjs +2 -0
  18. package/dist/gaugeit.min.cjs.map +7 -0
  19. package/dist/gaugeit.min.css +1 -0
  20. package/dist/gaugeit.standalone.js +4720 -0
  21. package/dist/gaugeit.standalone.js.map +7 -0
  22. package/dist/gaugeit.standalone.min.js +302 -0
  23. package/dist/gaugeit.standalone.min.js.map +7 -0
  24. package/dist/gaugeit.umd.js +4713 -0
  25. package/dist/gaugeit.umd.js.map +7 -0
  26. package/dist/gaugeit.umd.min.js +4 -0
  27. package/dist/gaugeit.umd.min.js.map +7 -0
  28. package/dist/manifest.json +24 -0
  29. package/docs/api.md +456 -0
  30. package/docs/architecture.md +224 -0
  31. package/docs/assets/arc_zones.png +0 -0
  32. package/docs/assets/center_zero.png +0 -0
  33. package/docs/assets/classic_instrument.png +0 -0
  34. package/docs/assets/classic_linear_center_zero.png +0 -0
  35. package/docs/assets/classic_linear_instrument.png +0 -0
  36. package/docs/assets/heritage_rolling_tape.png +0 -0
  37. package/docs/assets/heritage_round.png +0 -0
  38. package/docs/assets/line_scale.png +0 -0
  39. package/docs/assets/rose_balance.png +0 -0
  40. package/docs/creating-gauge-types.md +286 -0
  41. package/docs/development.md +56 -0
  42. package/docs/framework-integration.md +84 -0
  43. 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
@@ -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.