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,4720 @@
|
|
|
1
|
+
/*! Gaugeit.js v0.1.0 | MIT License | https://github.com/kasperikoski/gaugeit.js#readme */
|
|
2
|
+
var Gaugeit = (() => {
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/standalone.js
|
|
22
|
+
var standalone_exports = {};
|
|
23
|
+
__export(standalone_exports, {
|
|
24
|
+
Gauge: () => Gauge,
|
|
25
|
+
GaugeTypeRegistry: () => GaugeTypeRegistry,
|
|
26
|
+
VERSION: () => VERSION,
|
|
27
|
+
autoMount: () => autoMount,
|
|
28
|
+
createGauge: () => createGauge,
|
|
29
|
+
deepMerge: () => deepMerge,
|
|
30
|
+
default: () => standalone_default,
|
|
31
|
+
defineGaugeitElement: () => defineGaugeitElement,
|
|
32
|
+
easingNames: () => easingNames,
|
|
33
|
+
gaugeTypeRegistry: () => gaugeTypeRegistry,
|
|
34
|
+
geometry: () => geometry,
|
|
35
|
+
getMountedGauge: () => getMountedGauge,
|
|
36
|
+
layers: () => layers,
|
|
37
|
+
registerGaugeType: () => registerGaugeType,
|
|
38
|
+
types: () => types,
|
|
39
|
+
unmountGauge: () => unmountGauge
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// src/gaugeit.css
|
|
43
|
+
var gaugeit_default = '/* Gaugeit.js baseline styles. Visual geometry is rendered by SVG; CSS only handles layout and integration. */\n.gaugeit-host {\n display: block;\n min-width: 0;\n width: var(--gaugeit-width, auto);\n height: var(--gaugeit-height, auto);\n aspect-ratio: var(--gaugeit-aspect-ratio, auto);\n}\n\n.gaugeit {\n display: block;\n width: 100%;\n min-width: 0;\n color: var(--gaugeit-text-color, inherit);\n font-family: var(--gaugeit-font-family, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);\n contain: layout style;\n}\n\n.gaugeit__svg {\n display: block;\n width: 100%;\n max-width: 100%;\n height: auto;\n overflow: hidden;\n user-select: none;\n -webkit-user-select: none;\n touch-action: pan-y;\n}\n\n.gaugeit__pointer-rotating,\n.gaugeit__linear-indicator-moving,\n.gaugeit__tape-slot {\n will-change: transform;\n}\n\n.gaugeit__pointer,\n.gaugeit__pointer-cap,\n.gaugeit__linear-indicator,\n.gaugeit__linear-indicator-cap,\n.gaugeit__tape-window,\n.gaugeit__tape-window-inner,\n.gaugeit__tape-indicator,\n.gaugeit__zero-marker,\n.gaugeit__ticks,\n.gaugeit__zone,\n.gaugeit__track {\n shape-rendering: geometricPrecision;\n}\n\n.gaugeit__label,\n.gaugeit__readout,\n.gaugeit__tape-label {\n paint-order: stroke fill;\n font-variant-numeric: tabular-nums;\n}\n\ngauge-it {\n display: block;\n min-width: 0;\n}\n\n.gaugeit-element__mount {\n display: block;\n width: 100%;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .gaugeit__pointer-rotating,\n .gaugeit__linear-indicator-moving,\n .gaugeit__tape-slot {\n will-change: auto;\n }\n}\n\n@media (forced-colors: active) {\n .gaugeit__pointer,\n .gaugeit__pointer-cap,\n .gaugeit__linear-indicator,\n .gaugeit__linear-indicator-cap,\n .gaugeit__tape-window,\n .gaugeit__tape-window-inner,\n .gaugeit__tape-indicator,\n .gaugeit__zero-marker,\n .gaugeit__ticks,\n .gaugeit__label,\n .gaugeit__readout,\n .gaugeit__light {\n forced-color-adjust: auto;\n }\n}\n\n/* Intrinsic mode grows with the computed SVG viewBox; contain mode fits a fixed host box. */\n.gaugeit--layout-intrinsic {\n height: auto;\n}\n\n.gaugeit--layout-contain {\n width: 100%;\n height: 100%;\n min-height: 0;\n}\n\n.gaugeit--layout-contain .gaugeit__svg {\n width: 100%;\n height: 100%;\n max-height: 100%;\n}\n\n/* Shared SVG annunciator lamp. Colors are themeable through CSS variables or light.customColor. */\n.gaugeit__light {\n --gaugeit-light-core: #f59e0b;\n --gaugeit-light-highlight: #fde68a;\n --gaugeit-light-glow: #fbbf24;\n --gaugeit-light-rim: #92400e;\n --gaugeit-light-off: #3f2d12;\n --gaugeit-light-opacity: 1;\n pointer-events: none;\n opacity: var(--gaugeit-light-opacity);\n}\n\n.gaugeit__light--red {\n --gaugeit-light-core: #ef4444;\n --gaugeit-light-highlight: #fecaca;\n --gaugeit-light-glow: #f87171;\n --gaugeit-light-rim: #7f1d1d;\n --gaugeit-light-off: #3f1c1c;\n}\n\n.gaugeit__light--green {\n --gaugeit-light-core: #22c55e;\n --gaugeit-light-highlight: #bbf7d0;\n --gaugeit-light-glow: #4ade80;\n --gaugeit-light-rim: #14532d;\n --gaugeit-light-off: #182c1e;\n}\n\n.gaugeit__light--blue {\n --gaugeit-light-core: #3b82f6;\n --gaugeit-light-highlight: #bfdbfe;\n --gaugeit-light-glow: #60a5fa;\n --gaugeit-light-rim: #1e3a8a;\n --gaugeit-light-off: #1b2438;\n}\n\n.gaugeit__light--amber {\n --gaugeit-light-core: #f59e0b;\n --gaugeit-light-highlight: #fde68a;\n --gaugeit-light-glow: #fbbf24;\n --gaugeit-light-rim: #92400e;\n --gaugeit-light-off: #3f2d12;\n}\n\n.gaugeit__light--yellow {\n --gaugeit-light-core: #fde047;\n --gaugeit-light-highlight: #fef9c3;\n --gaugeit-light-glow: #facc15;\n --gaugeit-light-rim: #a16207;\n --gaugeit-light-off: #443a16;\n}\n\n.gaugeit__light--orange {\n --gaugeit-light-core: #f97316;\n --gaugeit-light-highlight: #fed7aa;\n --gaugeit-light-glow: #fb923c;\n --gaugeit-light-rim: #9a3412;\n --gaugeit-light-off: #462515;\n}\n\n.gaugeit__light--white {\n --gaugeit-light-core: #f8fafc;\n --gaugeit-light-highlight: #ffffff;\n --gaugeit-light-glow: #e2e8f0;\n --gaugeit-light-rim: #64748b;\n --gaugeit-light-off: #3f4955;\n}\n\n.gaugeit__light--halogen {\n --gaugeit-light-core: #fff2bf;\n --gaugeit-light-highlight: #fffdf0;\n --gaugeit-light-glow: #ffe79a;\n --gaugeit-light-rim: #9a7b24;\n --gaugeit-light-off: #4d4326;\n}\n\n.gaugeit__light--cyan {\n --gaugeit-light-core: #22d3ee;\n --gaugeit-light-highlight: #cffafe;\n --gaugeit-light-glow: #67e8f9;\n --gaugeit-light-rim: #155e75;\n --gaugeit-light-off: #18343a;\n}\n\n.gaugeit__light--brown {\n --gaugeit-light-core: #a16207;\n --gaugeit-light-highlight: #f5d0a1;\n --gaugeit-light-glow: #d6a56b;\n --gaugeit-light-rim: #5b3b10;\n --gaugeit-light-off: #332617;\n}\n\n.gaugeit__light-emission {\n pointer-events: none;\n transition: opacity 150ms ease;\n}\n\n.gaugeit__light-emission--off {\n opacity: var(--gaugeit-light-opacity, 1);\n}\n\n.gaugeit__light-emission--on {\n opacity: 0;\n}\n\n.gaugeit__light-glow {\n opacity: 0.76;\n transform-box: fill-box;\n transform-origin: center;\n}\n\n.gaugeit__light:not(.has-glow) .gaugeit__light-glow {\n display: none;\n}\n\n.gaugeit__light-bezel,\n.gaugeit__light-bezel-highlight,\n.gaugeit__light-bulb,\n.gaugeit__light-lens-shade,\n.gaugeit__light-sparkle {\n shape-rendering: geometricPrecision;\n}\n\n.gaugeit__light-bulb {\n transform-box: fill-box;\n transform-origin: center;\n transition: filter 150ms ease;\n}\n\n.gaugeit__light.is-on:not(.is-blinking) .gaugeit__light-emission--off {\n opacity: 0;\n}\n\n.gaugeit__light.is-on:not(.is-blinking) .gaugeit__light-emission--on {\n opacity: var(--gaugeit-light-opacity, 1);\n}\n\n.gaugeit__light.is-on .gaugeit__light-bulb--on {\n filter: saturate(1.08) brightness(1.05);\n}\n\n.gaugeit__light.is-pulsing .gaugeit__light-glow {\n animation: gaugeit-light-pulse-glow var(--gaugeit-light-pulse-duration, 2600ms) ease-in-out infinite;\n}\n\n.gaugeit__light.is-pulsing .gaugeit__light-bulb--on {\n animation: gaugeit-light-pulse-lens var(--gaugeit-light-pulse-duration, 2600ms) ease-in-out infinite;\n}\n\n.gaugeit__light.is-on.is-blinking .gaugeit__light-emission--on {\n animation: gaugeit-light-blink-on var(--gaugeit-light-blink-duration, 1000ms) step-end infinite;\n}\n\n.gaugeit__light.is-on.is-blinking .gaugeit__light-emission--off {\n animation: gaugeit-light-blink-off var(--gaugeit-light-blink-duration, 1000ms) step-end infinite;\n}\n\n@keyframes gaugeit-light-pulse-glow {\n 0%, 100% { opacity: 0.72; transform: scale(0.99); }\n 50% { opacity: 0.8; transform: scale(1.015); }\n}\n\n@keyframes gaugeit-light-pulse-lens {\n 0%, 100% { filter: saturate(1.06) brightness(1.04); }\n 50% { filter: saturate(1.1) brightness(1.07); }\n}\n\n@keyframes gaugeit-light-blink-on {\n 0%, 48% { opacity: var(--gaugeit-light-opacity, 1); }\n 49%, 100% { opacity: 0; }\n}\n\n@keyframes gaugeit-light-blink-off {\n 0%, 48% { opacity: 0; }\n 49%, 100% { opacity: var(--gaugeit-light-opacity, 1); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .gaugeit__light.is-blinking .gaugeit__light-emission,\n .gaugeit__light.is-pulsing .gaugeit__light-glow,\n .gaugeit__light.is-pulsing .gaugeit__light-bulb--on {\n animation: none;\n }\n\n .gaugeit__light.is-on.is-blinking .gaugeit__light-emission--off {\n opacity: 0;\n }\n\n .gaugeit__light.is-on.is-blinking .gaugeit__light-emission--on {\n opacity: var(--gaugeit-light-opacity, 1);\n }\n}\n';
|
|
44
|
+
|
|
45
|
+
// src/core/utils.js
|
|
46
|
+
function isPlainObject(value) {
|
|
47
|
+
if (value === null || typeof value !== "object") return false;
|
|
48
|
+
if (Object.prototype.toString.call(value) !== "[object Object]") return false;
|
|
49
|
+
const prototype = Object.getPrototypeOf(value);
|
|
50
|
+
if (prototype === null || prototype === Object.prototype) return true;
|
|
51
|
+
const constructor = Object.prototype.hasOwnProperty.call(prototype, "constructor") ? prototype.constructor : null;
|
|
52
|
+
return typeof constructor === "function" && constructor.name === "Object";
|
|
53
|
+
}
|
|
54
|
+
function deepMerge(base, override) {
|
|
55
|
+
if (!isPlainObject(base)) return cloneValue(override);
|
|
56
|
+
const result = { ...base };
|
|
57
|
+
if (!isPlainObject(override)) return result;
|
|
58
|
+
for (const [key, value] of Object.entries(override)) {
|
|
59
|
+
if (isUnsafeObjectKey(key)) continue;
|
|
60
|
+
if (isPlainObject(value) && isPlainObject(result[key])) {
|
|
61
|
+
result[key] = deepMerge(result[key], value);
|
|
62
|
+
} else {
|
|
63
|
+
result[key] = cloneValue(value);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
function cloneValue(value) {
|
|
69
|
+
if (Array.isArray(value)) return value.map(cloneValue);
|
|
70
|
+
if (isPlainObject(value)) {
|
|
71
|
+
const copy = {};
|
|
72
|
+
for (const [key, child] of Object.entries(value)) {
|
|
73
|
+
if (!isUnsafeObjectKey(key)) copy[key] = cloneValue(child);
|
|
74
|
+
}
|
|
75
|
+
return copy;
|
|
76
|
+
}
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
function isUnsafeObjectKey(key) {
|
|
80
|
+
return key === "__proto__" || key === "prototype" || key === "constructor";
|
|
81
|
+
}
|
|
82
|
+
function clamp(value, min, max) {
|
|
83
|
+
return Math.min(max, Math.max(min, value));
|
|
84
|
+
}
|
|
85
|
+
function finiteNumber(value, fallback) {
|
|
86
|
+
const number = Number(value);
|
|
87
|
+
return Number.isFinite(number) ? number : fallback;
|
|
88
|
+
}
|
|
89
|
+
var idCounter = 0;
|
|
90
|
+
function uniqueId(prefix = "gaugeit") {
|
|
91
|
+
idCounter += 1;
|
|
92
|
+
return `${prefix}-${idCounter}`;
|
|
93
|
+
}
|
|
94
|
+
function resolveElement(target) {
|
|
95
|
+
if (typeof target === "string") {
|
|
96
|
+
const element = document.querySelector(target);
|
|
97
|
+
if (!element) throw new Error(`Gaugeit target not found: ${target}`);
|
|
98
|
+
return element;
|
|
99
|
+
}
|
|
100
|
+
if (target && typeof target === "object" && target.nodeType === 1) return target;
|
|
101
|
+
throw new TypeError("Gaugeit target must be an Element or a valid CSS selector.");
|
|
102
|
+
}
|
|
103
|
+
function isTransparentColor(color) {
|
|
104
|
+
if (color === null || color === void 0) return true;
|
|
105
|
+
const normalized = String(color).trim().toLowerCase();
|
|
106
|
+
return normalized === "" || normalized === "transparent" || normalized === "none";
|
|
107
|
+
}
|
|
108
|
+
function requestFrame(callback) {
|
|
109
|
+
if (typeof requestAnimationFrame === "function") return requestAnimationFrame(callback);
|
|
110
|
+
return setTimeout(() => callback(Date.now()), 16);
|
|
111
|
+
}
|
|
112
|
+
function cancelFrame(handle) {
|
|
113
|
+
if (handle === null || handle === void 0) return;
|
|
114
|
+
if (typeof cancelAnimationFrame === "function") {
|
|
115
|
+
cancelAnimationFrame(handle);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
clearTimeout(handle);
|
|
119
|
+
}
|
|
120
|
+
function dispatchGaugeEvent(target, name, detail) {
|
|
121
|
+
if (!target || typeof target.dispatchEvent !== "function" || typeof CustomEvent !== "function") return;
|
|
122
|
+
target.dispatchEvent(new CustomEvent(`gaugeit:${name}`, { detail }));
|
|
123
|
+
}
|
|
124
|
+
function parseJsonObject(value, fallback = {}) {
|
|
125
|
+
if (typeof value !== "string" || value.trim() === "") return fallback;
|
|
126
|
+
try {
|
|
127
|
+
const parsed = JSON.parse(value);
|
|
128
|
+
return isPlainObject(parsed) ? parsed : fallback;
|
|
129
|
+
} catch (e) {
|
|
130
|
+
return fallback;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/geometry/scale.js
|
|
135
|
+
function degreesToRadians(degrees) {
|
|
136
|
+
return degrees * Math.PI / 180;
|
|
137
|
+
}
|
|
138
|
+
function polarPoint(centerX, centerY, radius, angleDegrees) {
|
|
139
|
+
const radians = degreesToRadians(angleDegrees);
|
|
140
|
+
return {
|
|
141
|
+
x: centerX + radius * Math.cos(radians),
|
|
142
|
+
y: centerY + radius * Math.sin(radians)
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function gaugeSweep(options) {
|
|
146
|
+
return options.geometry.endAngle - options.geometry.startAngle;
|
|
147
|
+
}
|
|
148
|
+
function centeredArcAngles(sweep, centerAngle = 0) {
|
|
149
|
+
const safeSweep = Math.min(360, Math.max(1e-3, Math.abs(finiteNumber(sweep, 180))));
|
|
150
|
+
const svgCenter = 270 + finiteNumber(centerAngle, 0);
|
|
151
|
+
const halfSweep = safeSweep / 2;
|
|
152
|
+
return {
|
|
153
|
+
startAngle: svgCenter - halfSweep,
|
|
154
|
+
endAngle: svgCenter + halfSweep
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function valueToAngle(value, options, clampValue = true) {
|
|
158
|
+
const min = options.min;
|
|
159
|
+
const max = options.max;
|
|
160
|
+
const normalizedValue = clampValue ? clamp(value, min, max) : value;
|
|
161
|
+
const valueRatio = (normalizedValue - min) / (max - min);
|
|
162
|
+
const visualRatio = options.invert ? 1 - valueRatio : valueRatio;
|
|
163
|
+
return options.geometry.startAngle + gaugeSweep(options) * visualRatio;
|
|
164
|
+
}
|
|
165
|
+
function angleToValue(angle, options, clampResult = true) {
|
|
166
|
+
const sweep = gaugeSweep(options);
|
|
167
|
+
const visualRatio = (angle - options.geometry.startAngle) / sweep;
|
|
168
|
+
const valueRatio = options.invert ? 1 - visualRatio : visualRatio;
|
|
169
|
+
const value = options.min + valueRatio * (options.max - options.min);
|
|
170
|
+
return clampResult ? clamp(value, options.min, options.max) : value;
|
|
171
|
+
}
|
|
172
|
+
function arcPath(centerX, centerY, radius, startAngle, endAngle) {
|
|
173
|
+
const start = polarPoint(centerX, centerY, radius, startAngle);
|
|
174
|
+
const end = polarPoint(centerX, centerY, radius, endAngle);
|
|
175
|
+
const delta = endAngle - startAngle;
|
|
176
|
+
const sweepFlag = delta >= 0 ? 1 : 0;
|
|
177
|
+
if (Math.abs(delta) >= 360 - 1e-3) {
|
|
178
|
+
const midpoint = polarPoint(centerX, centerY, radius, startAngle + delta / 2);
|
|
179
|
+
return [
|
|
180
|
+
`M ${roundSvg(start.x)} ${roundSvg(start.y)}`,
|
|
181
|
+
`A ${roundSvg(radius)} ${roundSvg(radius)} 0 1 ${sweepFlag} ${roundSvg(midpoint.x)} ${roundSvg(midpoint.y)}`,
|
|
182
|
+
`A ${roundSvg(radius)} ${roundSvg(radius)} 0 1 ${sweepFlag} ${roundSvg(end.x)} ${roundSvg(end.y)}`
|
|
183
|
+
].join(" ");
|
|
184
|
+
}
|
|
185
|
+
const largeArcFlag = Math.abs(delta) > 180 ? 1 : 0;
|
|
186
|
+
return [
|
|
187
|
+
`M ${roundSvg(start.x)} ${roundSvg(start.y)}`,
|
|
188
|
+
`A ${roundSvg(radius)} ${roundSvg(radius)} 0 ${largeArcFlag} ${sweepFlag} ${roundSvg(end.x)} ${roundSvg(end.y)}`
|
|
189
|
+
].join(" ");
|
|
190
|
+
}
|
|
191
|
+
function taperedArcPath(centerX, centerY, radius, startAngle, endAngle, startWidth, endWidth, segments = 48) {
|
|
192
|
+
const delta = endAngle - startAngle;
|
|
193
|
+
const sampleCount = Math.max(2, Math.min(256, Math.ceil(finiteNumber(segments, 48))));
|
|
194
|
+
const safeRadius = Math.max(0, finiteNumber(radius, 0));
|
|
195
|
+
const firstWidth = Math.max(0, finiteNumber(startWidth, 0));
|
|
196
|
+
const lastWidth = Math.max(0, finiteNumber(endWidth, firstWidth));
|
|
197
|
+
const outer = [];
|
|
198
|
+
const inner = [];
|
|
199
|
+
for (let index = 0; index <= sampleCount; index += 1) {
|
|
200
|
+
const ratio = index / sampleCount;
|
|
201
|
+
const angle = startAngle + delta * ratio;
|
|
202
|
+
const width = firstWidth + (lastWidth - firstWidth) * ratio;
|
|
203
|
+
outer.push(polarPoint(centerX, centerY, safeRadius + width / 2, angle));
|
|
204
|
+
inner.push(polarPoint(centerX, centerY, Math.max(0, safeRadius - width / 2), angle));
|
|
205
|
+
}
|
|
206
|
+
const commands = [`M ${roundSvg(outer[0].x)} ${roundSvg(outer[0].y)}`];
|
|
207
|
+
for (let index = 1; index < outer.length; index += 1) {
|
|
208
|
+
commands.push(`L ${roundSvg(outer[index].x)} ${roundSvg(outer[index].y)}`);
|
|
209
|
+
}
|
|
210
|
+
for (let index = inner.length - 1; index >= 0; index -= 1) {
|
|
211
|
+
commands.push(`L ${roundSvg(inner[index].x)} ${roundSvg(inner[index].y)}`);
|
|
212
|
+
}
|
|
213
|
+
commands.push("Z");
|
|
214
|
+
return commands.join(" ");
|
|
215
|
+
}
|
|
216
|
+
function niceInterval(range, desiredDivisions = 5) {
|
|
217
|
+
const safeRange = Math.abs(finiteNumber(range, 1)) || 1;
|
|
218
|
+
const raw = safeRange / Math.max(1, desiredDivisions);
|
|
219
|
+
const exponent = Math.floor(Math.log10(raw));
|
|
220
|
+
const fraction = raw / Math.pow(10, exponent);
|
|
221
|
+
let niceFraction;
|
|
222
|
+
if (fraction <= 1) niceFraction = 1;
|
|
223
|
+
else if (fraction <= 2) niceFraction = 2;
|
|
224
|
+
else if (fraction <= 2.5) niceFraction = 2.5;
|
|
225
|
+
else if (fraction <= 5) niceFraction = 5;
|
|
226
|
+
else niceFraction = 10;
|
|
227
|
+
return niceFraction * Math.pow(10, exponent);
|
|
228
|
+
}
|
|
229
|
+
function buildScaleValues(min, max, interval) {
|
|
230
|
+
const safeInterval = Math.abs(finiteNumber(interval, 0));
|
|
231
|
+
if (safeInterval <= 0 || max <= min) return [min, max];
|
|
232
|
+
const precision = Math.min(12, Math.max(0, decimalPlaces(safeInterval) + 2));
|
|
233
|
+
const epsilon = safeInterval / 1e6;
|
|
234
|
+
const values = [];
|
|
235
|
+
let current = min;
|
|
236
|
+
let guard = 0;
|
|
237
|
+
while (current <= max + epsilon && guard < 1e4) {
|
|
238
|
+
values.push(roundTo(current, precision));
|
|
239
|
+
current += safeInterval;
|
|
240
|
+
guard += 1;
|
|
241
|
+
}
|
|
242
|
+
if (values.length === 0 || Math.abs(values[values.length - 1] - max) > epsilon) {
|
|
243
|
+
values.push(max);
|
|
244
|
+
} else {
|
|
245
|
+
values[values.length - 1] = max;
|
|
246
|
+
}
|
|
247
|
+
return values;
|
|
248
|
+
}
|
|
249
|
+
function buildMinorValues(majorValues, subdivisions) {
|
|
250
|
+
const count = Math.max(0, Math.floor(finiteNumber(subdivisions, 0)));
|
|
251
|
+
if (count === 0 || majorValues.length < 2) return [];
|
|
252
|
+
const values = [];
|
|
253
|
+
for (let index = 0; index < majorValues.length - 1; index += 1) {
|
|
254
|
+
const start = majorValues[index];
|
|
255
|
+
const end = majorValues[index + 1];
|
|
256
|
+
const step = (end - start) / (count + 1);
|
|
257
|
+
for (let subdivision = 1; subdivision <= count; subdivision += 1) {
|
|
258
|
+
values.push(start + step * subdivision);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return values;
|
|
262
|
+
}
|
|
263
|
+
function resolveZoneSegments(min, max, zones) {
|
|
264
|
+
const normalized = Array.isArray(zones) ? zones.filter((zone) => zone && typeof zone === "object").map((zone, index) => ({
|
|
265
|
+
...zone,
|
|
266
|
+
min: clamp(finiteNumber(zone.min, min), min, max),
|
|
267
|
+
max: clamp(finiteNumber(zone.max, max), min, max),
|
|
268
|
+
_index: index
|
|
269
|
+
})).filter((zone) => zone.max > zone.min) : [];
|
|
270
|
+
const breakpoints = /* @__PURE__ */ new Set([min, max]);
|
|
271
|
+
for (const zone of normalized) {
|
|
272
|
+
breakpoints.add(zone.min);
|
|
273
|
+
breakpoints.add(zone.max);
|
|
274
|
+
}
|
|
275
|
+
const points = [...breakpoints].sort((left, right) => left - right);
|
|
276
|
+
const segments = [];
|
|
277
|
+
for (let index = 0; index < points.length - 1; index += 1) {
|
|
278
|
+
const segmentMin = points[index];
|
|
279
|
+
const segmentMax = points[index + 1];
|
|
280
|
+
const midpoint = (segmentMin + segmentMax) / 2;
|
|
281
|
+
const candidates = normalized.filter((zone2) => midpoint >= zone2.min && midpoint <= zone2.max);
|
|
282
|
+
const zone = candidates.length ? candidates[candidates.length - 1] : null;
|
|
283
|
+
segments.push({ min: segmentMin, max: segmentMax, zone });
|
|
284
|
+
}
|
|
285
|
+
return segments;
|
|
286
|
+
}
|
|
287
|
+
function findZoneForValue(value, zones) {
|
|
288
|
+
if (!Array.isArray(zones)) return null;
|
|
289
|
+
let match = null;
|
|
290
|
+
for (const zone of zones) {
|
|
291
|
+
if (!zone || typeof zone !== "object") continue;
|
|
292
|
+
if (value >= Number(zone.min) && value <= Number(zone.max)) match = zone;
|
|
293
|
+
}
|
|
294
|
+
return match;
|
|
295
|
+
}
|
|
296
|
+
function roundSvg(value) {
|
|
297
|
+
return Math.round(value * 1e3) / 1e3;
|
|
298
|
+
}
|
|
299
|
+
function roundTo(value, precision) {
|
|
300
|
+
const factor = Math.pow(10, precision);
|
|
301
|
+
return Math.round(value * factor) / factor;
|
|
302
|
+
}
|
|
303
|
+
function decimalPlaces(value) {
|
|
304
|
+
const text = String(value).toLowerCase();
|
|
305
|
+
if (text.includes("e-")) return Number(text.split("e-")[1]);
|
|
306
|
+
const decimal = text.split(".")[1];
|
|
307
|
+
return decimal ? decimal.length : 0;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// src/geometry/linear.js
|
|
311
|
+
function linearYToSvg(y, options) {
|
|
312
|
+
const linear = options.linear || {};
|
|
313
|
+
const originY = finiteNumber(linear.originY, 140);
|
|
314
|
+
return originY - finiteNumber(y, 0);
|
|
315
|
+
}
|
|
316
|
+
function linearAxis(options) {
|
|
317
|
+
const linear = options.linear || {};
|
|
318
|
+
const startX = finiteNumber(linear.startX, 36);
|
|
319
|
+
const startY = linearYToSvg(linear.startY, options);
|
|
320
|
+
let endX = finiteNumber(linear.endX, 284);
|
|
321
|
+
let endY = linearYToSvg(linear.endY, options);
|
|
322
|
+
let dx = endX - startX;
|
|
323
|
+
let dy = endY - startY;
|
|
324
|
+
let length = Math.hypot(dx, dy);
|
|
325
|
+
if (length < 1e-3) {
|
|
326
|
+
endX = startX + 1;
|
|
327
|
+
endY = startY;
|
|
328
|
+
dx = 1;
|
|
329
|
+
dy = 0;
|
|
330
|
+
length = 1;
|
|
331
|
+
}
|
|
332
|
+
const tangentX = dx / length;
|
|
333
|
+
const tangentY = dy / length;
|
|
334
|
+
return {
|
|
335
|
+
startX,
|
|
336
|
+
startY,
|
|
337
|
+
endX,
|
|
338
|
+
endY,
|
|
339
|
+
dx,
|
|
340
|
+
dy,
|
|
341
|
+
length,
|
|
342
|
+
tangentX,
|
|
343
|
+
tangentY,
|
|
344
|
+
normalX: -tangentY,
|
|
345
|
+
normalY: tangentX,
|
|
346
|
+
angle: Math.atan2(dy, dx) * 180 / Math.PI
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function valueToLinearRatio(value, options, clampValue = true) {
|
|
350
|
+
const normalizedValue = clampValue ? clamp(value, options.min, options.max) : value;
|
|
351
|
+
const valueRatio = (normalizedValue - options.min) / (options.max - options.min);
|
|
352
|
+
return options.invert ? 1 - valueRatio : valueRatio;
|
|
353
|
+
}
|
|
354
|
+
function valueToLinearPoint(value, options, clampValue = true) {
|
|
355
|
+
const axis = linearAxis(options);
|
|
356
|
+
const ratio = valueToLinearRatio(value, options, clampValue);
|
|
357
|
+
return {
|
|
358
|
+
x: axis.startX + axis.dx * ratio,
|
|
359
|
+
y: axis.startY + axis.dy * ratio
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function linearPointToValue(x, y, options, clampResult = true) {
|
|
363
|
+
const axis = linearAxis(options);
|
|
364
|
+
const relativeX = finiteNumber(x, axis.startX) - axis.startX;
|
|
365
|
+
const relativeY = finiteNumber(y, axis.startY) - axis.startY;
|
|
366
|
+
const visualRatio = (relativeX * axis.tangentX + relativeY * axis.tangentY) / axis.length;
|
|
367
|
+
const boundedVisualRatio = clampResult ? clamp(visualRatio, 0, 1) : visualRatio;
|
|
368
|
+
const valueRatio = options.invert ? 1 - boundedVisualRatio : boundedVisualRatio;
|
|
369
|
+
const value = options.min + valueRatio * (options.max - options.min);
|
|
370
|
+
return clampResult ? clamp(value, options.min, options.max) : value;
|
|
371
|
+
}
|
|
372
|
+
function offsetLinearPoint(point, axis, distance) {
|
|
373
|
+
const amount = finiteNumber(distance, 0);
|
|
374
|
+
return {
|
|
375
|
+
x: point.x + axis.normalX * amount,
|
|
376
|
+
y: point.y + axis.normalY * amount
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
function linearNormalOffset(value) {
|
|
380
|
+
return -finiteNumber(value, 0);
|
|
381
|
+
}
|
|
382
|
+
function linearIndicatorNormalOffset(indicator) {
|
|
383
|
+
return linearNormalOffset(indicator == null ? void 0 : indicator.centerOffset);
|
|
384
|
+
}
|
|
385
|
+
function linearNormalSpan(length, position = "cross", offset = 0) {
|
|
386
|
+
const size = Math.max(0, finiteNumber(length, 0));
|
|
387
|
+
const shift = finiteNumber(offset, 0);
|
|
388
|
+
if (position === "negative") return { start: shift - size, end: shift };
|
|
389
|
+
if (position === "positive") return { start: shift, end: shift + size };
|
|
390
|
+
return { start: shift - size / 2, end: shift + size / 2 };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// src/geometry/tape.js
|
|
394
|
+
function tapeWindowBox(options) {
|
|
395
|
+
const tape = options.tape || {};
|
|
396
|
+
return {
|
|
397
|
+
x: finiteNumber(tape.x, 64),
|
|
398
|
+
y: finiteNumber(tape.y, 58),
|
|
399
|
+
width: Math.max(20, finiteNumber(tape.width, 192)),
|
|
400
|
+
height: Math.max(20, finiteNumber(tape.height, 210))
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
function resolveTapeInterval(options) {
|
|
404
|
+
var _a, _b, _c;
|
|
405
|
+
const explicit = finiteNumber((_a = options.tape) == null ? void 0 : _a.interval, 0);
|
|
406
|
+
if (explicit > 0) return explicit;
|
|
407
|
+
return niceInterval(options.max - options.min, Math.max(2, ((_c = (_b = options.scale) == null ? void 0 : _b.major) == null ? void 0 : _c.divisions) || 5));
|
|
408
|
+
}
|
|
409
|
+
function tapeMinorStep(options) {
|
|
410
|
+
var _a;
|
|
411
|
+
const subdivisions = Math.max(0, Math.floor(finiteNumber((_a = options.tape) == null ? void 0 : _a.minorSubdivisions, 4)));
|
|
412
|
+
return resolveTapeInterval(options) / (subdivisions + 1);
|
|
413
|
+
}
|
|
414
|
+
function tapeMinorSpacing(options) {
|
|
415
|
+
var _a, _b;
|
|
416
|
+
const subdivisions = Math.max(0, Math.floor(finiteNumber((_a = options.tape) == null ? void 0 : _a.minorSubdivisions, 4)));
|
|
417
|
+
return Math.max(2, finiteNumber((_b = options.tape) == null ? void 0 : _b.majorSpacing, 54)) / (subdivisions + 1);
|
|
418
|
+
}
|
|
419
|
+
function tapeValueOffset(value, currentValue, options) {
|
|
420
|
+
const step = tapeMinorStep(options);
|
|
421
|
+
const spacing = tapeMinorSpacing(options);
|
|
422
|
+
const direction = options.invert ? -1 : 1;
|
|
423
|
+
return (Number(value) - Number(currentValue)) / step * spacing * direction;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// src/geometry/light.js
|
|
427
|
+
function resolveLightPosition(light, options) {
|
|
428
|
+
return {
|
|
429
|
+
x: finiteNumber(light == null ? void 0 : light.x, options.geometry.width / 2),
|
|
430
|
+
y: options.geometry.mode === "linear" ? linearYToSvg(light == null ? void 0 : light.y, options) : finiteNumber(light == null ? void 0 : light.y, options.geometry.height / 2)
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function lightIsActive(light, value) {
|
|
434
|
+
if (!(light == null ? void 0 : light.visible)) return false;
|
|
435
|
+
const trigger = light.trigger || {};
|
|
436
|
+
const sample = finiteNumber(value, 0);
|
|
437
|
+
switch (trigger.mode) {
|
|
438
|
+
case "below":
|
|
439
|
+
return sample <= finiteNumber(trigger.value, 0);
|
|
440
|
+
case "above":
|
|
441
|
+
return sample >= finiteNumber(trigger.value, 0);
|
|
442
|
+
case "between": {
|
|
443
|
+
const first = finiteNumber(trigger.min, 0);
|
|
444
|
+
const second = finiteNumber(trigger.max, first);
|
|
445
|
+
return sample >= Math.min(first, second) && sample <= Math.max(first, second);
|
|
446
|
+
}
|
|
447
|
+
case "manual":
|
|
448
|
+
default:
|
|
449
|
+
return light.on === true;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// src/geometry/bounds.js
|
|
454
|
+
var EPSILON = 1e-3;
|
|
455
|
+
function createBounds(x = Infinity, y = Infinity, width = -Infinity, height = -Infinity) {
|
|
456
|
+
if (Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(width) && Number.isFinite(height)) {
|
|
457
|
+
return { x, y, width, height };
|
|
458
|
+
}
|
|
459
|
+
return { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity };
|
|
460
|
+
}
|
|
461
|
+
function includePoint(bounds, x, y) {
|
|
462
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) return bounds;
|
|
463
|
+
bounds.minX = Math.min(bounds.minX, x);
|
|
464
|
+
bounds.minY = Math.min(bounds.minY, y);
|
|
465
|
+
bounds.maxX = Math.max(bounds.maxX, x);
|
|
466
|
+
bounds.maxY = Math.max(bounds.maxY, y);
|
|
467
|
+
return bounds;
|
|
468
|
+
}
|
|
469
|
+
function includeRect(bounds, x, y, width, height) {
|
|
470
|
+
includePoint(bounds, x, y);
|
|
471
|
+
includePoint(bounds, x + Math.max(0, width), y + Math.max(0, height));
|
|
472
|
+
return bounds;
|
|
473
|
+
}
|
|
474
|
+
function finalizeBounds(bounds, fallback = { x: 0, y: 0, width: 1, height: 1 }) {
|
|
475
|
+
if (!bounds || !Number.isFinite(bounds.minX) || !Number.isFinite(bounds.minY) || !Number.isFinite(bounds.maxX) || !Number.isFinite(bounds.maxY)) {
|
|
476
|
+
return { ...fallback };
|
|
477
|
+
}
|
|
478
|
+
return {
|
|
479
|
+
x: bounds.minX,
|
|
480
|
+
y: bounds.minY,
|
|
481
|
+
width: Math.max(1, bounds.maxX - bounds.minX),
|
|
482
|
+
height: Math.max(1, bounds.maxY - bounds.minY)
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
function padBounds(bounds, padding = 0) {
|
|
486
|
+
const amount = Math.max(0, finiteNumber(padding, 0));
|
|
487
|
+
return {
|
|
488
|
+
x: bounds.x - amount,
|
|
489
|
+
y: bounds.y - amount,
|
|
490
|
+
width: Math.max(1, bounds.width + amount * 2),
|
|
491
|
+
height: Math.max(1, bounds.height + amount * 2)
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
function unionBounds(...rectangles) {
|
|
495
|
+
const accumulator = createBounds();
|
|
496
|
+
for (const rectangle of rectangles) {
|
|
497
|
+
if (!rectangle) continue;
|
|
498
|
+
includeRect(accumulator, rectangle.x, rectangle.y, rectangle.width, rectangle.height);
|
|
499
|
+
}
|
|
500
|
+
return finalizeBounds(accumulator);
|
|
501
|
+
}
|
|
502
|
+
function isClosedGauge(options) {
|
|
503
|
+
return Math.abs(gaugeSweep(options)) >= 360 - EPSILON;
|
|
504
|
+
}
|
|
505
|
+
function resolveScaleEndpointValues(values, options) {
|
|
506
|
+
var _a;
|
|
507
|
+
const mode = ((_a = options.scale) == null ? void 0 : _a.endpointMode) || "auto";
|
|
508
|
+
if (values.length < 2 || mode === "both") return values;
|
|
509
|
+
if (mode === "dedupe" || mode === "auto" && isClosedGauge(options)) return values.slice(0, -1);
|
|
510
|
+
return values;
|
|
511
|
+
}
|
|
512
|
+
function resolveReadoutPosition(part, options) {
|
|
513
|
+
const reference = readoutReferenceBox(options);
|
|
514
|
+
const fontSize = Math.max(1, finiteNumber(part.fontSize, 12));
|
|
515
|
+
const margin = Math.max(0, finiteNumber(part.margin, 12));
|
|
516
|
+
const position = part.position || "top";
|
|
517
|
+
const automaticX = part.x === "auto";
|
|
518
|
+
const automaticY = part.y === "auto";
|
|
519
|
+
const x = automaticX ? reference.x + reference.width / 2 : finiteNumber(part.x, reference.x + reference.width / 2);
|
|
520
|
+
let y;
|
|
521
|
+
if (automaticY) {
|
|
522
|
+
if (position === "bottom") y = reference.y + reference.height - margin - fontSize / 2;
|
|
523
|
+
else if (position === "center") y = reference.y + reference.height / 2;
|
|
524
|
+
else y = reference.y + margin + fontSize / 2;
|
|
525
|
+
} else if (options.geometry.mode === "linear") {
|
|
526
|
+
y = linearYToSvg(part.y, options);
|
|
527
|
+
} else {
|
|
528
|
+
y = finiteNumber(part.y, reference.y + margin + fontSize / 2);
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
// Offsets refine automatic placement. Numeric coordinates are final, absolute
|
|
532
|
+
// SVG positions and therefore do not inherit a type preset's auto offset.
|
|
533
|
+
x: x + (automaticX ? finiteNumber(part.offsetX, 0) : 0),
|
|
534
|
+
y: y + (automaticY ? finiteNumber(part.offsetY, 0) : 0)
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
function resolveFaceBox(options, contentBounds = null) {
|
|
538
|
+
var _a, _b;
|
|
539
|
+
const face = options.face;
|
|
540
|
+
const geometry2 = options.geometry;
|
|
541
|
+
if (face.fit !== "content" || !contentBounds) {
|
|
542
|
+
return {
|
|
543
|
+
x: face.x,
|
|
544
|
+
y: face.y,
|
|
545
|
+
width: face.width,
|
|
546
|
+
height: face.height
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
const padding = Math.max(0, finiteNumber(face.contentPadding, 8));
|
|
550
|
+
const configured = { x: face.x, y: face.y, width: face.width, height: face.height };
|
|
551
|
+
const minimumWidth = (_a = face.minWidth) != null ? _a : configured.width;
|
|
552
|
+
const minimumHeight = (_b = face.minHeight) != null ? _b : configured.height;
|
|
553
|
+
if (face.shape === "circle") {
|
|
554
|
+
const cx = geometry2.centerX;
|
|
555
|
+
const cy = geometry2.centerY;
|
|
556
|
+
const configuredRadius = Math.min(minimumWidth, minimumHeight) / 2;
|
|
557
|
+
const contentRadius = Math.max(
|
|
558
|
+
cx - contentBounds.x,
|
|
559
|
+
contentBounds.x + contentBounds.width - cx,
|
|
560
|
+
cy - contentBounds.y,
|
|
561
|
+
contentBounds.y + contentBounds.height - cy
|
|
562
|
+
) + padding;
|
|
563
|
+
const radius = Math.max(1, configuredRadius, contentRadius);
|
|
564
|
+
return { x: cx - radius, y: cy - radius, width: radius * 2, height: radius * 2 };
|
|
565
|
+
}
|
|
566
|
+
const padded = {
|
|
567
|
+
x: contentBounds.x - padding,
|
|
568
|
+
y: contentBounds.y - padding,
|
|
569
|
+
width: contentBounds.width + padding * 2,
|
|
570
|
+
height: contentBounds.height + padding * 2
|
|
571
|
+
};
|
|
572
|
+
const horizontalExtent = Math.max(
|
|
573
|
+
geometry2.centerX - padded.x,
|
|
574
|
+
padded.x + padded.width - geometry2.centerX
|
|
575
|
+
);
|
|
576
|
+
const width = Math.max(1, minimumWidth, horizontalExtent * 2);
|
|
577
|
+
const height = Math.max(1, minimumHeight, padded.height);
|
|
578
|
+
const contentCenterY = padded.y + padded.height / 2;
|
|
579
|
+
return {
|
|
580
|
+
x: geometry2.centerX - width / 2,
|
|
581
|
+
y: contentCenterY - height / 2,
|
|
582
|
+
width,
|
|
583
|
+
height
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
function calculateGaugeBounds(options, configuration = {}) {
|
|
587
|
+
var _a;
|
|
588
|
+
const includeFace = configuration.includeFace !== false;
|
|
589
|
+
const includeBase = configuration.includeBase === void 0 ? ((_a = options.layout) == null ? void 0 : _a.includeGeometry) !== false : configuration.includeBase === true;
|
|
590
|
+
const contentBounds = configuration.contentBounds || null;
|
|
591
|
+
const geometry2 = options.geometry;
|
|
592
|
+
const bounds = createBounds();
|
|
593
|
+
if (includeBase) includeRect(bounds, 0, 0, geometry2.width, geometry2.height);
|
|
594
|
+
if (includeFace && options.face.visible) {
|
|
595
|
+
const box = resolveFaceBox(options, contentBounds);
|
|
596
|
+
const expansion = options.face.shadow ? 10 : Math.max(0, options.face.strokeWidth / 2);
|
|
597
|
+
includeRect(bounds, box.x - expansion, box.y - expansion, box.width + expansion * 2, box.height + expansion * 2);
|
|
598
|
+
if (options.face.clipContent) {
|
|
599
|
+
return finalizeBounds(bounds, { x: 0, y: 0, width: geometry2.width, height: geometry2.height });
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
if (geometry2.mode === "linear") {
|
|
603
|
+
includeLinearTrackAndZones(bounds, options);
|
|
604
|
+
includeLinearScale(bounds, options);
|
|
605
|
+
includeLinearLabels(bounds, options);
|
|
606
|
+
includeLinearIndicator(bounds, options);
|
|
607
|
+
includeZeroMarker(bounds, options);
|
|
608
|
+
} else if (geometry2.mode === "tape") {
|
|
609
|
+
includeTape(bounds, options);
|
|
610
|
+
} else {
|
|
611
|
+
includeTrackAndZones(bounds, options);
|
|
612
|
+
includeScale(bounds, options);
|
|
613
|
+
includeLabels(bounds, options);
|
|
614
|
+
includeZeroMarker(bounds, options);
|
|
615
|
+
includePointer(bounds, options);
|
|
616
|
+
}
|
|
617
|
+
includeReadout(bounds, options);
|
|
618
|
+
includeLight(bounds, options);
|
|
619
|
+
return finalizeBounds(bounds, { x: 0, y: 0, width: geometry2.width, height: geometry2.height });
|
|
620
|
+
}
|
|
621
|
+
function includeTrackAndZones(bounds, options) {
|
|
622
|
+
var _a, _b;
|
|
623
|
+
const { geometry: geometry2, track } = options;
|
|
624
|
+
if (track.visible) {
|
|
625
|
+
includeArc(bounds, geometry2.centerX, geometry2.centerY, track.radius, track.width, geometry2.startAngle, geometry2.endAngle);
|
|
626
|
+
}
|
|
627
|
+
for (const zone of options.zones || []) {
|
|
628
|
+
if (isTransparentColor(zone.color)) continue;
|
|
629
|
+
includeArc(
|
|
630
|
+
bounds,
|
|
631
|
+
geometry2.centerX,
|
|
632
|
+
geometry2.centerY,
|
|
633
|
+
(_b = (_a = zone.radius) != null ? _a : track.radius) != null ? _b : geometry2.radius,
|
|
634
|
+
zoneMaximumWidth(zone, track.width),
|
|
635
|
+
valueToAngle(zone.min, options),
|
|
636
|
+
valueToAngle(zone.max, options)
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
function zoneMaximumWidth(zone, fallbackWidth) {
|
|
641
|
+
var _a, _b, _c;
|
|
642
|
+
if (!(zone == null ? void 0 : zone.taper)) return (_a = zone == null ? void 0 : zone.width) != null ? _a : fallbackWidth;
|
|
643
|
+
return Math.max(
|
|
644
|
+
0,
|
|
645
|
+
finiteNumber(zone.taper.startWidth, (_b = zone.width) != null ? _b : fallbackWidth),
|
|
646
|
+
finiteNumber(zone.taper.endWidth, (_c = zone.width) != null ? _c : fallbackWidth)
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
function includeScale(bounds, options) {
|
|
650
|
+
const { geometry: geometry2, scale } = options;
|
|
651
|
+
const lengths = [];
|
|
652
|
+
if (scale.major.visible) lengths.push(scale.major.length);
|
|
653
|
+
if (scale.minor.visible) lengths.push(scale.minor.length);
|
|
654
|
+
if (!lengths.length) return;
|
|
655
|
+
const length = Math.max(...lengths, 0);
|
|
656
|
+
let inner = scale.radius;
|
|
657
|
+
let outer = scale.radius;
|
|
658
|
+
if (scale.position === "outside") outer += length;
|
|
659
|
+
else if (scale.position === "cross") {
|
|
660
|
+
inner -= length / 2;
|
|
661
|
+
outer += length / 2;
|
|
662
|
+
} else inner -= length;
|
|
663
|
+
includeArcSector(bounds, geometry2.centerX, geometry2.centerY, Math.max(0, inner), outer, geometry2.startAngle, geometry2.endAngle);
|
|
664
|
+
}
|
|
665
|
+
function includeLabels(bounds, options) {
|
|
666
|
+
const labels = options.labels;
|
|
667
|
+
if (!labels.visible) return;
|
|
668
|
+
const interval = Number(labels.interval) > 0 ? Number(labels.interval) : Math.abs(options.max - options.min) / Math.max(1, options.scale.major.divisions || 5);
|
|
669
|
+
const values = resolveScaleEndpointValues(buildScaleValues(options.min, options.max, interval), options);
|
|
670
|
+
for (const value of values) {
|
|
671
|
+
const point = polarPoint(options.geometry.centerX, options.geometry.centerY, labels.radius, valueToAngle(value, options));
|
|
672
|
+
let rendered = Number(value).toFixed(labels.fractionDigits);
|
|
673
|
+
if (typeof labels.formatter === "function") {
|
|
674
|
+
try {
|
|
675
|
+
rendered = String(labels.formatter(value, { min: options.min, max: options.max, options }));
|
|
676
|
+
} catch (e) {
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
const halfWidth = Math.max(labels.fontSize * 0.55, rendered.length * labels.fontSize * 0.34);
|
|
680
|
+
const halfHeight = labels.fontSize * 0.68;
|
|
681
|
+
includeRect(bounds, point.x - halfWidth, point.y - halfHeight, halfWidth * 2, halfHeight * 2);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
function includePointer(bounds, options) {
|
|
685
|
+
const { geometry: geometry2, pointer } = options;
|
|
686
|
+
if (!pointer.visible) return;
|
|
687
|
+
const expansion = Math.max(pointer.width / 2, pointer.strokeWidth / 2, 1);
|
|
688
|
+
includeArcSector(
|
|
689
|
+
bounds,
|
|
690
|
+
geometry2.centerX,
|
|
691
|
+
geometry2.centerY,
|
|
692
|
+
0,
|
|
693
|
+
pointer.length + expansion,
|
|
694
|
+
geometry2.startAngle,
|
|
695
|
+
geometry2.endAngle
|
|
696
|
+
);
|
|
697
|
+
if (pointer.tailLength > 0) {
|
|
698
|
+
includeArcSector(
|
|
699
|
+
bounds,
|
|
700
|
+
geometry2.centerX,
|
|
701
|
+
geometry2.centerY,
|
|
702
|
+
0,
|
|
703
|
+
pointer.tailLength + expansion,
|
|
704
|
+
geometry2.startAngle + 180,
|
|
705
|
+
geometry2.endAngle + 180
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
if (pointer.cap.visible) {
|
|
709
|
+
const radius = pointer.cap.radius + pointer.cap.strokeWidth / 2;
|
|
710
|
+
includeRect(bounds, geometry2.centerX - radius, geometry2.centerY - radius, radius * 2, radius * 2);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function includeLinearTrackAndZones(bounds, options) {
|
|
714
|
+
var _a, _b;
|
|
715
|
+
const axis = linearAxis(options);
|
|
716
|
+
if (options.track.visible) {
|
|
717
|
+
includeLinearStroke(
|
|
718
|
+
bounds,
|
|
719
|
+
axis,
|
|
720
|
+
valueToLinearPoint(options.min, options),
|
|
721
|
+
valueToLinearPoint(options.max, options),
|
|
722
|
+
linearNormalOffset(options.linear.zoneOffset),
|
|
723
|
+
options.track.width
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
for (const zone of options.zones || []) {
|
|
727
|
+
if (isTransparentColor(zone.color)) continue;
|
|
728
|
+
includeLinearStroke(
|
|
729
|
+
bounds,
|
|
730
|
+
axis,
|
|
731
|
+
valueToLinearPoint(zone.min, options),
|
|
732
|
+
valueToLinearPoint(zone.max, options),
|
|
733
|
+
linearNormalOffset((_a = zone.offset) != null ? _a : options.linear.zoneOffset),
|
|
734
|
+
(_b = zone.width) != null ? _b : options.track.width
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function includeLinearScale(bounds, options) {
|
|
739
|
+
const axis = linearAxis(options);
|
|
740
|
+
const lengths = [];
|
|
741
|
+
if (options.scale.major.visible) lengths.push(options.scale.major.length);
|
|
742
|
+
if (options.scale.minor.visible) lengths.push(options.scale.minor.length);
|
|
743
|
+
if (!lengths.length) return;
|
|
744
|
+
const span = linearNormalSpan(Math.max(...lengths, 0), options.linear.tickSide);
|
|
745
|
+
includeLinearExtent(bounds, axis, span.start, span.end);
|
|
746
|
+
}
|
|
747
|
+
function includeLinearLabels(bounds, options) {
|
|
748
|
+
const labels = options.labels;
|
|
749
|
+
if (!labels.visible) return;
|
|
750
|
+
const interval = Number(labels.interval) > 0 ? Number(labels.interval) : Math.abs(options.max - options.min) / Math.max(1, options.scale.major.divisions || 5);
|
|
751
|
+
const values = buildScaleValues(options.min, options.max, interval);
|
|
752
|
+
const axis = linearAxis(options);
|
|
753
|
+
for (const value of values) {
|
|
754
|
+
const point = offsetLinearPoint(
|
|
755
|
+
valueToLinearPoint(value, options),
|
|
756
|
+
axis,
|
|
757
|
+
linearNormalOffset(options.linear.labelOffset)
|
|
758
|
+
);
|
|
759
|
+
let rendered = Number(value).toFixed(labels.fractionDigits);
|
|
760
|
+
if (typeof labels.formatter === "function") {
|
|
761
|
+
try {
|
|
762
|
+
rendered = String(labels.formatter(value, { min: options.min, max: options.max, options }));
|
|
763
|
+
} catch (e) {
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
const halfWidth = Math.max(labels.fontSize * 0.55, rendered.length * labels.fontSize * 0.34);
|
|
767
|
+
const halfHeight = labels.fontSize * 0.68;
|
|
768
|
+
includeRect(bounds, point.x - halfWidth, point.y - halfHeight, halfWidth * 2, halfHeight * 2);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
function includeLinearIndicator(bounds, options) {
|
|
772
|
+
const indicator = options.indicator;
|
|
773
|
+
if (!indicator.visible) return;
|
|
774
|
+
const axis = linearAxis(options);
|
|
775
|
+
const span = linearNormalSpan(
|
|
776
|
+
indicator.length,
|
|
777
|
+
indicator.position,
|
|
778
|
+
linearIndicatorNormalOffset(indicator)
|
|
779
|
+
);
|
|
780
|
+
const strokeExpansion = Math.max(indicator.width / 2, indicator.strokeWidth / 2, 1);
|
|
781
|
+
const normalStart = Math.min(span.start, span.end) - strokeExpansion;
|
|
782
|
+
const normalEnd = Math.max(span.start, span.end) + strokeExpansion;
|
|
783
|
+
let tangentExpansion = strokeExpansion;
|
|
784
|
+
if (indicator.type === "marker") tangentExpansion = Math.max(tangentExpansion, indicator.markerSize);
|
|
785
|
+
if (indicator.type === "carriage") tangentExpansion = Math.max(tangentExpansion, indicator.carriageWidth / 2);
|
|
786
|
+
if (indicator.cap.visible) tangentExpansion = Math.max(tangentExpansion, indicator.cap.radius + indicator.cap.strokeWidth / 2);
|
|
787
|
+
includeLinearExtent(bounds, axis, normalStart, normalEnd, tangentExpansion);
|
|
788
|
+
}
|
|
789
|
+
function includeZeroMarker(bounds, options) {
|
|
790
|
+
var _a, _b;
|
|
791
|
+
const marker = (_a = options.centerZero) == null ? void 0 : _a.marker;
|
|
792
|
+
if (!(marker == null ? void 0 : marker.visible)) return;
|
|
793
|
+
const expansion = Math.max(marker.width / 2, 0.5);
|
|
794
|
+
if (options.geometry.mode === "linear") {
|
|
795
|
+
const axis = linearAxis(options);
|
|
796
|
+
const point = valueToLinearPoint(options.centerZero.zeroValue, options);
|
|
797
|
+
const span = linearNormalSpan(
|
|
798
|
+
marker.length,
|
|
799
|
+
marker.position,
|
|
800
|
+
linearNormalOffset(marker.centerOffset)
|
|
801
|
+
);
|
|
802
|
+
const start2 = offsetLinearPoint(point, axis, span.start);
|
|
803
|
+
const end2 = offsetLinearPoint(point, axis, span.end);
|
|
804
|
+
includeRect(
|
|
805
|
+
bounds,
|
|
806
|
+
Math.min(start2.x, end2.x) - expansion,
|
|
807
|
+
Math.min(start2.y, end2.y) - expansion,
|
|
808
|
+
Math.abs(end2.x - start2.x) + expansion * 2,
|
|
809
|
+
Math.abs(end2.y - start2.y) + expansion * 2
|
|
810
|
+
);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
const angle = valueToAngle(options.centerZero.zeroValue, options);
|
|
814
|
+
const radius = (_b = marker.radius) != null ? _b : options.scale.radius;
|
|
815
|
+
const start = polarPoint(
|
|
816
|
+
options.geometry.centerX,
|
|
817
|
+
options.geometry.centerY,
|
|
818
|
+
Math.max(0, radius - marker.length),
|
|
819
|
+
angle
|
|
820
|
+
);
|
|
821
|
+
const end = polarPoint(options.geometry.centerX, options.geometry.centerY, radius, angle);
|
|
822
|
+
includeRect(
|
|
823
|
+
bounds,
|
|
824
|
+
Math.min(start.x, end.x) - expansion,
|
|
825
|
+
Math.min(start.y, end.y) - expansion,
|
|
826
|
+
Math.abs(end.x - start.x) + expansion * 2,
|
|
827
|
+
Math.abs(end.y - start.y) + expansion * 2
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
function includeLinearStroke(bounds, axis, startPoint, endPoint, offset, width) {
|
|
831
|
+
const halfWidth = Math.max(0, finiteNumber(width, 0)) / 2;
|
|
832
|
+
const start = offsetLinearPoint(startPoint, axis, offset);
|
|
833
|
+
const end = offsetLinearPoint(endPoint, axis, offset);
|
|
834
|
+
const minX = Math.min(start.x, end.x) - halfWidth;
|
|
835
|
+
const minY = Math.min(start.y, end.y) - halfWidth;
|
|
836
|
+
includeRect(
|
|
837
|
+
bounds,
|
|
838
|
+
minX,
|
|
839
|
+
minY,
|
|
840
|
+
Math.abs(end.x - start.x) + halfWidth * 2,
|
|
841
|
+
Math.abs(end.y - start.y) + halfWidth * 2
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
function includeLinearExtent(bounds, axis, normalStart, normalEnd, tangentExpansion = 0) {
|
|
845
|
+
for (const base of [
|
|
846
|
+
{ x: axis.startX, y: axis.startY },
|
|
847
|
+
{ x: axis.endX, y: axis.endY }
|
|
848
|
+
]) {
|
|
849
|
+
for (const normal of [normalStart, normalEnd]) {
|
|
850
|
+
const point = offsetLinearPoint(base, axis, normal);
|
|
851
|
+
includePoint(
|
|
852
|
+
bounds,
|
|
853
|
+
point.x - axis.tangentX * tangentExpansion,
|
|
854
|
+
point.y - axis.tangentY * tangentExpansion
|
|
855
|
+
);
|
|
856
|
+
includePoint(
|
|
857
|
+
bounds,
|
|
858
|
+
point.x + axis.tangentX * tangentExpansion,
|
|
859
|
+
point.y + axis.tangentY * tangentExpansion
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
function includeTape(bounds, options) {
|
|
865
|
+
const box = tapeWindowBox(options);
|
|
866
|
+
const expansion = Math.max(
|
|
867
|
+
options.tape.windowStrokeWidth / 2,
|
|
868
|
+
options.tape.windowInnerStrokeWidth / 2
|
|
869
|
+
);
|
|
870
|
+
includeRect(
|
|
871
|
+
bounds,
|
|
872
|
+
box.x - expansion,
|
|
873
|
+
box.y - expansion,
|
|
874
|
+
box.width + expansion * 2,
|
|
875
|
+
box.height + expansion * 2
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
function includeLight(bounds, options) {
|
|
879
|
+
const light = options.light;
|
|
880
|
+
if (!(light == null ? void 0 : light.visible)) return;
|
|
881
|
+
const point = resolveLightPosition(light, options);
|
|
882
|
+
const radius = Math.max(1, finiteNumber(light.radius, 8));
|
|
883
|
+
const halo = light.glow ? radius * 2.96 : radius;
|
|
884
|
+
const bezel = radius + Math.max(1.5, radius * 0.18);
|
|
885
|
+
const extent = Math.max(halo, bezel);
|
|
886
|
+
includeRect(bounds, point.x - extent, point.y - extent, extent * 2, extent * 2);
|
|
887
|
+
}
|
|
888
|
+
function includeReadout(bounds, options) {
|
|
889
|
+
if (!options.readout.visible) return;
|
|
890
|
+
for (const [kind, part] of Object.entries(options.readout)) {
|
|
891
|
+
if (kind === "visible" || !(part == null ? void 0 : part.visible)) continue;
|
|
892
|
+
const text = kind === "value" ? `${part.prefix || ""}${options.max}${part.suffix || ""}` : String(part.text || "");
|
|
893
|
+
if (!text && kind !== "value") continue;
|
|
894
|
+
const point = resolveReadoutPosition(part, options);
|
|
895
|
+
const halfWidth = Math.max(part.fontSize * 0.55, text.length * part.fontSize * 0.34);
|
|
896
|
+
const halfHeight = part.fontSize * 0.68;
|
|
897
|
+
includeRect(bounds, point.x - halfWidth, point.y - halfHeight, halfWidth * 2, halfHeight * 2);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
function readoutReferenceBox(options) {
|
|
901
|
+
const face = options.face;
|
|
902
|
+
if (face.visible && face.fit !== "content") return { x: face.x, y: face.y, width: face.width, height: face.height };
|
|
903
|
+
return { x: 0, y: 0, width: options.geometry.width, height: options.geometry.height };
|
|
904
|
+
}
|
|
905
|
+
function includeArc(bounds, cx, cy, radius, width, startAngle, endAngle) {
|
|
906
|
+
const halfWidth = Math.max(0, finiteNumber(width, 0)) / 2;
|
|
907
|
+
includeArcSector(bounds, cx, cy, Math.max(0, radius - halfWidth), radius + halfWidth, startAngle, endAngle);
|
|
908
|
+
}
|
|
909
|
+
function includeArcSector(bounds, cx, cy, innerRadius, outerRadius, startAngle, endAngle) {
|
|
910
|
+
const angles = [startAngle, endAngle];
|
|
911
|
+
for (const cardinal of [0, 90, 180, 270]) {
|
|
912
|
+
if (angleIsWithinSweep(cardinal, startAngle, endAngle)) angles.push(cardinal);
|
|
913
|
+
}
|
|
914
|
+
if (innerRadius <= EPSILON) includePoint(bounds, cx, cy);
|
|
915
|
+
for (const angle of angles) {
|
|
916
|
+
const outer = polarPoint(cx, cy, Math.max(0, outerRadius), angle);
|
|
917
|
+
includePoint(bounds, outer.x, outer.y);
|
|
918
|
+
if (innerRadius > EPSILON) {
|
|
919
|
+
const inner = polarPoint(cx, cy, innerRadius, angle);
|
|
920
|
+
includePoint(bounds, inner.x, inner.y);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
function angleIsWithinSweep(angle, startAngle, endAngle) {
|
|
925
|
+
const delta = endAngle - startAngle;
|
|
926
|
+
if (Math.abs(delta) >= 360 - EPSILON) return true;
|
|
927
|
+
if (delta >= 0) return positiveModulo(angle - startAngle, 360) <= delta + EPSILON;
|
|
928
|
+
return positiveModulo(startAngle - angle, 360) <= -delta + EPSILON;
|
|
929
|
+
}
|
|
930
|
+
function positiveModulo(value, divisor) {
|
|
931
|
+
return (value % divisor + divisor) % divisor;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
// src/renderer/svg.js
|
|
935
|
+
var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
936
|
+
function svgElement(tagName, attributes = {}, text = null) {
|
|
937
|
+
const element = document.createElementNS(SVG_NAMESPACE, tagName);
|
|
938
|
+
setSvgAttributes(element, attributes);
|
|
939
|
+
if (text !== null && text !== void 0) element.textContent = String(text);
|
|
940
|
+
return element;
|
|
941
|
+
}
|
|
942
|
+
function setSvgAttributes(element, attributes) {
|
|
943
|
+
for (const [name, value] of Object.entries(attributes || {})) {
|
|
944
|
+
if (value === null || value === void 0 || value === false) continue;
|
|
945
|
+
if (name === "className") element.setAttribute("class", String(value));
|
|
946
|
+
else element.setAttribute(name, String(value));
|
|
947
|
+
}
|
|
948
|
+
return element;
|
|
949
|
+
}
|
|
950
|
+
var SvgRenderer = class {
|
|
951
|
+
constructor(host, options) {
|
|
952
|
+
this.host = host;
|
|
953
|
+
this.options = options;
|
|
954
|
+
this.id = uniqueId("gaugeit");
|
|
955
|
+
this.controllers = [];
|
|
956
|
+
this.references = /* @__PURE__ */ new Map();
|
|
957
|
+
this.root = null;
|
|
958
|
+
this.svg = null;
|
|
959
|
+
this.content = null;
|
|
960
|
+
this.contentBounds = null;
|
|
961
|
+
this.layoutBounds = null;
|
|
962
|
+
this.contentClipPathId = null;
|
|
963
|
+
}
|
|
964
|
+
/** Creates a clean SVG root for a full render. */
|
|
965
|
+
begin() {
|
|
966
|
+
this.destroyControllers();
|
|
967
|
+
this.references.clear();
|
|
968
|
+
this.contentClipPathId = null;
|
|
969
|
+
const root = document.createElement("div");
|
|
970
|
+
root.className = [
|
|
971
|
+
"gaugeit",
|
|
972
|
+
`gaugeit--${this.options.type}`,
|
|
973
|
+
`gaugeit--layout-${this.options.layout.mode}`,
|
|
974
|
+
this.options.className
|
|
975
|
+
].filter(Boolean).join(" ");
|
|
976
|
+
root.dataset.gaugeitRoot = "true";
|
|
977
|
+
root.dataset.gaugeitLayout = this.options.layout.mode;
|
|
978
|
+
this.contentBounds = calculateGaugeBounds(this.options, { includeFace: false, includeBase: false });
|
|
979
|
+
this.layoutBounds = calculateGaugeBounds(this.options, { contentBounds: this.contentBounds });
|
|
980
|
+
const initialViewport = padBounds(this.layoutBounds, this.options.layout.padding);
|
|
981
|
+
const geometry2 = this.options.geometry;
|
|
982
|
+
const svg = svgElement("svg", {
|
|
983
|
+
class: "gaugeit__svg",
|
|
984
|
+
viewBox: viewBoxValue(initialViewport),
|
|
985
|
+
preserveAspectRatio: geometry2.preserveAspectRatio,
|
|
986
|
+
focusable: "false"
|
|
987
|
+
});
|
|
988
|
+
const title = svgElement("title", { id: `${this.id}-title` }, this.options.accessibility.label);
|
|
989
|
+
svg.append(title);
|
|
990
|
+
if (this.options.accessibility.description) {
|
|
991
|
+
const description = svgElement(
|
|
992
|
+
"desc",
|
|
993
|
+
{ id: `${this.id}-description` },
|
|
994
|
+
this.options.accessibility.description
|
|
995
|
+
);
|
|
996
|
+
svg.append(description);
|
|
997
|
+
svg.setAttribute("aria-describedby", description.id);
|
|
998
|
+
}
|
|
999
|
+
svg.setAttribute("aria-labelledby", title.id);
|
|
1000
|
+
const content = svgElement("g", {
|
|
1001
|
+
class: "gaugeit__viewport",
|
|
1002
|
+
"data-gaugeit-viewport": "true"
|
|
1003
|
+
});
|
|
1004
|
+
svg.append(content);
|
|
1005
|
+
root.append(svg);
|
|
1006
|
+
this.host.replaceChildren(root);
|
|
1007
|
+
this.host.classList.add("gaugeit-host");
|
|
1008
|
+
this.root = root;
|
|
1009
|
+
this.svg = svg;
|
|
1010
|
+
this.content = content;
|
|
1011
|
+
this.applySizing(initialViewport);
|
|
1012
|
+
return svg;
|
|
1013
|
+
}
|
|
1014
|
+
/** Finalizes the responsive viewBox after all layers have been rendered. */
|
|
1015
|
+
finish() {
|
|
1016
|
+
if (!this.svg || !this.content) return null;
|
|
1017
|
+
const measured = measureContent(this.content);
|
|
1018
|
+
const contentIsClipped = this.options.face.visible && this.options.face.clipContent;
|
|
1019
|
+
const combined = measured && !contentIsClipped ? unionBounds(this.layoutBounds, measured) : this.layoutBounds;
|
|
1020
|
+
const viewport = padBounds(combined, this.options.layout.padding);
|
|
1021
|
+
this.layoutBounds = combined;
|
|
1022
|
+
this.svg.setAttribute("viewBox", viewBoxValue(viewport));
|
|
1023
|
+
this.applySizing(viewport);
|
|
1024
|
+
this.root.dataset.gaugeitViewBox = viewBoxValue(viewport);
|
|
1025
|
+
return viewport;
|
|
1026
|
+
}
|
|
1027
|
+
/** Creates an SVG group for one logical layer. */
|
|
1028
|
+
group(name, attributes = {}) {
|
|
1029
|
+
const clipPath = name !== "face" && this.contentClipPathId ? `url(#${this.contentClipPathId})` : void 0;
|
|
1030
|
+
const group = svgElement("g", {
|
|
1031
|
+
class: `gaugeit__layer gaugeit__layer--${name}`,
|
|
1032
|
+
"data-gaugeit-layer": name,
|
|
1033
|
+
"clip-path": clipPath,
|
|
1034
|
+
...attributes
|
|
1035
|
+
});
|
|
1036
|
+
this.content.append(group);
|
|
1037
|
+
return group;
|
|
1038
|
+
}
|
|
1039
|
+
/** Clips subsequently created non-face layers to a reusable SVG clip path. */
|
|
1040
|
+
setContentClipPath(clipPathId) {
|
|
1041
|
+
this.contentClipPathId = clipPathId ? String(clipPathId) : null;
|
|
1042
|
+
}
|
|
1043
|
+
/** Returns conservative content bounds available before layer rendering. */
|
|
1044
|
+
getContentBounds() {
|
|
1045
|
+
return this.contentBounds;
|
|
1046
|
+
}
|
|
1047
|
+
/** Stores a generated node or value for later dynamic updates. */
|
|
1048
|
+
setReference(name, value) {
|
|
1049
|
+
this.references.set(name, value);
|
|
1050
|
+
return value;
|
|
1051
|
+
}
|
|
1052
|
+
getReference(name) {
|
|
1053
|
+
return this.references.get(name);
|
|
1054
|
+
}
|
|
1055
|
+
addController(controller) {
|
|
1056
|
+
if (controller && typeof controller.update === "function") this.controllers.push(controller);
|
|
1057
|
+
}
|
|
1058
|
+
update(value, state) {
|
|
1059
|
+
for (const controller of this.controllers) controller.update(value, state);
|
|
1060
|
+
}
|
|
1061
|
+
destroyControllers() {
|
|
1062
|
+
for (const controller of this.controllers) {
|
|
1063
|
+
if (typeof controller.destroy === "function") controller.destroy();
|
|
1064
|
+
}
|
|
1065
|
+
this.controllers = [];
|
|
1066
|
+
}
|
|
1067
|
+
destroy() {
|
|
1068
|
+
this.destroyControllers();
|
|
1069
|
+
this.references.clear();
|
|
1070
|
+
if (this.root && this.root.parentNode === this.host) this.root.remove();
|
|
1071
|
+
this.host.classList.remove("gaugeit-host");
|
|
1072
|
+
this.root = null;
|
|
1073
|
+
this.svg = null;
|
|
1074
|
+
this.content = null;
|
|
1075
|
+
this.contentBounds = null;
|
|
1076
|
+
this.layoutBounds = null;
|
|
1077
|
+
this.contentClipPathId = null;
|
|
1078
|
+
}
|
|
1079
|
+
applySizing(viewport) {
|
|
1080
|
+
const responsive = this.options.responsive;
|
|
1081
|
+
const contain = this.options.layout.mode === "contain";
|
|
1082
|
+
this.root.style.width = responsive ? "100%" : `${viewport.width}px`;
|
|
1083
|
+
this.root.style.maxWidth = "100%";
|
|
1084
|
+
this.root.style.height = contain ? "100%" : "auto";
|
|
1085
|
+
this.svg.style.display = "block";
|
|
1086
|
+
this.svg.style.width = responsive ? "100%" : `${viewport.width}px`;
|
|
1087
|
+
this.svg.style.height = contain ? "100%" : "auto";
|
|
1088
|
+
this.svg.style.maxWidth = "100%";
|
|
1089
|
+
this.svg.style.maxHeight = contain ? "100%" : "none";
|
|
1090
|
+
this.svg.style.overflow = "hidden";
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
function viewBoxValue(bounds) {
|
|
1094
|
+
return `${round(bounds.x)} ${round(bounds.y)} ${round(bounds.width)} ${round(bounds.height)}`;
|
|
1095
|
+
}
|
|
1096
|
+
function round(value) {
|
|
1097
|
+
return Math.round(value * 1e3) / 1e3;
|
|
1098
|
+
}
|
|
1099
|
+
function measureContent(content) {
|
|
1100
|
+
if (typeof content.getBBox !== "function") return null;
|
|
1101
|
+
try {
|
|
1102
|
+
const box = content.getBBox();
|
|
1103
|
+
if (![box.x, box.y, box.width, box.height].every(Number.isFinite)) return null;
|
|
1104
|
+
if (box.width <= 0 && box.height <= 0) return null;
|
|
1105
|
+
return { x: box.x, y: box.y, width: Math.max(1, box.width), height: Math.max(1, box.height) };
|
|
1106
|
+
} catch (e) {
|
|
1107
|
+
return null;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// src/core/easing.js
|
|
1112
|
+
var EASINGS = {
|
|
1113
|
+
linear: (t) => t,
|
|
1114
|
+
easeInQuad: (t) => t * t,
|
|
1115
|
+
easeOutQuad: (t) => t * (2 - t),
|
|
1116
|
+
easeInOutQuad: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
|
|
1117
|
+
easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),
|
|
1118
|
+
easeInOutCubic: (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2,
|
|
1119
|
+
easeOutBack: (t) => {
|
|
1120
|
+
const c1 = 1.70158;
|
|
1121
|
+
const c3 = c1 + 1;
|
|
1122
|
+
return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
function resolveEasing(easing) {
|
|
1126
|
+
if (typeof easing === "function") return easing;
|
|
1127
|
+
return EASINGS[easing] || EASINGS.easeOutCubic;
|
|
1128
|
+
}
|
|
1129
|
+
var easingNames = Object.freeze(Object.keys(EASINGS));
|
|
1130
|
+
|
|
1131
|
+
// src/core/defaults.js
|
|
1132
|
+
var BASE_DEFAULTS = Object.freeze({
|
|
1133
|
+
type: "arc",
|
|
1134
|
+
min: 0,
|
|
1135
|
+
max: 100,
|
|
1136
|
+
value: 0,
|
|
1137
|
+
invert: false,
|
|
1138
|
+
overflow: "clamp",
|
|
1139
|
+
precision: 3,
|
|
1140
|
+
className: "",
|
|
1141
|
+
layout: {
|
|
1142
|
+
mode: "intrinsic",
|
|
1143
|
+
padding: 8,
|
|
1144
|
+
includeGeometry: true
|
|
1145
|
+
},
|
|
1146
|
+
geometry: {
|
|
1147
|
+
mode: "radial",
|
|
1148
|
+
width: 320,
|
|
1149
|
+
height: 220,
|
|
1150
|
+
centerX: 160,
|
|
1151
|
+
centerY: 174,
|
|
1152
|
+
radius: 120,
|
|
1153
|
+
startAngle: 180,
|
|
1154
|
+
endAngle: 360,
|
|
1155
|
+
preserveAspectRatio: "xMidYMid meet",
|
|
1156
|
+
aspectRatio: null
|
|
1157
|
+
},
|
|
1158
|
+
linear: {
|
|
1159
|
+
originY: 140,
|
|
1160
|
+
startX: 36,
|
|
1161
|
+
startY: 0,
|
|
1162
|
+
endX: 284,
|
|
1163
|
+
endY: 0,
|
|
1164
|
+
tickSide: "negative",
|
|
1165
|
+
labelOffset: 34,
|
|
1166
|
+
zoneOffset: -12
|
|
1167
|
+
},
|
|
1168
|
+
face: {
|
|
1169
|
+
visible: false,
|
|
1170
|
+
shape: "panel",
|
|
1171
|
+
fit: "geometry",
|
|
1172
|
+
contentPadding: 8,
|
|
1173
|
+
minWidth: null,
|
|
1174
|
+
minHeight: null,
|
|
1175
|
+
clipContent: false,
|
|
1176
|
+
x: 6,
|
|
1177
|
+
y: 6,
|
|
1178
|
+
width: 308,
|
|
1179
|
+
height: 208,
|
|
1180
|
+
cornerRadius: 18,
|
|
1181
|
+
color: "#ffffff",
|
|
1182
|
+
strokeColor: "#d1d5db",
|
|
1183
|
+
strokeWidth: 2,
|
|
1184
|
+
innerStrokeColor: "#f3f4f6",
|
|
1185
|
+
innerStrokeWidth: 1,
|
|
1186
|
+
shadow: false,
|
|
1187
|
+
glass: false
|
|
1188
|
+
},
|
|
1189
|
+
track: {
|
|
1190
|
+
visible: true,
|
|
1191
|
+
color: "#e5e7eb",
|
|
1192
|
+
width: 28,
|
|
1193
|
+
radius: 112,
|
|
1194
|
+
opacity: 1,
|
|
1195
|
+
lineCap: "butt",
|
|
1196
|
+
showUnderZones: false
|
|
1197
|
+
},
|
|
1198
|
+
zones: [],
|
|
1199
|
+
scale: {
|
|
1200
|
+
endpointMode: "auto",
|
|
1201
|
+
radius: 112,
|
|
1202
|
+
position: "inside",
|
|
1203
|
+
major: {
|
|
1204
|
+
visible: true,
|
|
1205
|
+
interval: null,
|
|
1206
|
+
divisions: 5,
|
|
1207
|
+
length: 13,
|
|
1208
|
+
width: 2,
|
|
1209
|
+
color: "#111827",
|
|
1210
|
+
opacity: 1
|
|
1211
|
+
},
|
|
1212
|
+
minor: {
|
|
1213
|
+
visible: true,
|
|
1214
|
+
subdivisions: 4,
|
|
1215
|
+
length: 7,
|
|
1216
|
+
width: 1,
|
|
1217
|
+
color: "#6b7280",
|
|
1218
|
+
opacity: 0.9
|
|
1219
|
+
}
|
|
1220
|
+
},
|
|
1221
|
+
labels: {
|
|
1222
|
+
visible: true,
|
|
1223
|
+
interval: null,
|
|
1224
|
+
radius: 84,
|
|
1225
|
+
color: "#111827",
|
|
1226
|
+
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
1227
|
+
fontSize: 11,
|
|
1228
|
+
fontWeight: 500,
|
|
1229
|
+
fractionDigits: 0,
|
|
1230
|
+
rotation: "horizontal",
|
|
1231
|
+
formatter: null
|
|
1232
|
+
},
|
|
1233
|
+
pointer: {
|
|
1234
|
+
visible: true,
|
|
1235
|
+
type: "needle",
|
|
1236
|
+
length: 94,
|
|
1237
|
+
shaftLength: null,
|
|
1238
|
+
tailLength: 13,
|
|
1239
|
+
width: 8,
|
|
1240
|
+
color: "#111827",
|
|
1241
|
+
strokeColor: "none",
|
|
1242
|
+
strokeWidth: 0,
|
|
1243
|
+
opacity: 1,
|
|
1244
|
+
colorByZone: false,
|
|
1245
|
+
cap: {
|
|
1246
|
+
visible: true,
|
|
1247
|
+
stackOrder: "above",
|
|
1248
|
+
radius: 8,
|
|
1249
|
+
color: "#111827",
|
|
1250
|
+
strokeColor: "#ffffff",
|
|
1251
|
+
strokeWidth: 2
|
|
1252
|
+
}
|
|
1253
|
+
},
|
|
1254
|
+
indicator: {
|
|
1255
|
+
visible: false,
|
|
1256
|
+
type: "hairline",
|
|
1257
|
+
position: "cross",
|
|
1258
|
+
length: 76,
|
|
1259
|
+
centerOffset: null,
|
|
1260
|
+
offset: 0,
|
|
1261
|
+
width: 2,
|
|
1262
|
+
color: "#b91c1c",
|
|
1263
|
+
strokeColor: "none",
|
|
1264
|
+
strokeWidth: 0,
|
|
1265
|
+
opacity: 1,
|
|
1266
|
+
lineCap: "round",
|
|
1267
|
+
colorByZone: false,
|
|
1268
|
+
markerSize: 8,
|
|
1269
|
+
carriageWidth: 14,
|
|
1270
|
+
carriageRadius: 4,
|
|
1271
|
+
cap: {
|
|
1272
|
+
visible: false,
|
|
1273
|
+
stackOrder: "above",
|
|
1274
|
+
radius: 4,
|
|
1275
|
+
color: "#b91c1c",
|
|
1276
|
+
strokeColor: "#ffffff",
|
|
1277
|
+
strokeWidth: 1
|
|
1278
|
+
}
|
|
1279
|
+
},
|
|
1280
|
+
centerZero: {
|
|
1281
|
+
zeroValue: 0,
|
|
1282
|
+
symmetric: false,
|
|
1283
|
+
marker: {
|
|
1284
|
+
visible: false,
|
|
1285
|
+
radius: null,
|
|
1286
|
+
position: "cross",
|
|
1287
|
+
centerOffset: 0,
|
|
1288
|
+
length: 20,
|
|
1289
|
+
width: 3,
|
|
1290
|
+
color: "#111827",
|
|
1291
|
+
opacity: 1,
|
|
1292
|
+
lineCap: "round"
|
|
1293
|
+
}
|
|
1294
|
+
},
|
|
1295
|
+
tape: {
|
|
1296
|
+
orientation: "vertical",
|
|
1297
|
+
x: 64,
|
|
1298
|
+
y: 58,
|
|
1299
|
+
width: 192,
|
|
1300
|
+
height: 210,
|
|
1301
|
+
cornerRadius: 12,
|
|
1302
|
+
windowColor: "#e5e7eb",
|
|
1303
|
+
windowStrokeColor: "#475569",
|
|
1304
|
+
windowStrokeWidth: 2,
|
|
1305
|
+
windowInnerStrokeColor: "#d1d5db",
|
|
1306
|
+
windowInnerStrokeWidth: 1,
|
|
1307
|
+
stripColor: "#ffffff",
|
|
1308
|
+
textColor: "#111827",
|
|
1309
|
+
tickColor: "#334155",
|
|
1310
|
+
minorTickColor: "#64748b",
|
|
1311
|
+
minorTickOpacity: 0.85,
|
|
1312
|
+
majorTickWidth: 2,
|
|
1313
|
+
minorTickWidth: 1,
|
|
1314
|
+
majorTickLength: 24,
|
|
1315
|
+
minorTickLength: 12,
|
|
1316
|
+
tickInset: 64,
|
|
1317
|
+
labelOffset: 10,
|
|
1318
|
+
interval: null,
|
|
1319
|
+
minorSubdivisions: 4,
|
|
1320
|
+
majorSpacing: 52,
|
|
1321
|
+
indicatorColor: "#b91c1c",
|
|
1322
|
+
indicatorWidth: 2.5,
|
|
1323
|
+
fadeSize: 32,
|
|
1324
|
+
fadeColor: "#ffffff",
|
|
1325
|
+
colorByZone: false
|
|
1326
|
+
},
|
|
1327
|
+
light: {
|
|
1328
|
+
visible: false,
|
|
1329
|
+
x: 160,
|
|
1330
|
+
y: 110,
|
|
1331
|
+
radius: 8,
|
|
1332
|
+
color: "amber",
|
|
1333
|
+
customColor: "",
|
|
1334
|
+
offColor: "#f3ead6",
|
|
1335
|
+
offEdgeColor: "#817768",
|
|
1336
|
+
bezelColor: "#4b5563",
|
|
1337
|
+
bezelHighlightColor: "#f8fafc",
|
|
1338
|
+
opacity: 1,
|
|
1339
|
+
on: false,
|
|
1340
|
+
glow: true,
|
|
1341
|
+
pulse: true,
|
|
1342
|
+
pulseInterval: 2600,
|
|
1343
|
+
blink: false,
|
|
1344
|
+
blinkInterval: 1e3,
|
|
1345
|
+
trigger: {
|
|
1346
|
+
mode: "manual",
|
|
1347
|
+
source: "target",
|
|
1348
|
+
value: 0,
|
|
1349
|
+
min: 0,
|
|
1350
|
+
max: 0
|
|
1351
|
+
}
|
|
1352
|
+
},
|
|
1353
|
+
readout: {
|
|
1354
|
+
visible: true,
|
|
1355
|
+
title: {
|
|
1356
|
+
visible: false,
|
|
1357
|
+
text: "",
|
|
1358
|
+
x: "auto",
|
|
1359
|
+
y: "auto",
|
|
1360
|
+
position: "top",
|
|
1361
|
+
margin: 12,
|
|
1362
|
+
offsetX: 0,
|
|
1363
|
+
offsetY: 0,
|
|
1364
|
+
color: "#374151",
|
|
1365
|
+
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
1366
|
+
fontSize: 14,
|
|
1367
|
+
fontWeight: 700,
|
|
1368
|
+
letterSpacing: 0.8
|
|
1369
|
+
},
|
|
1370
|
+
value: {
|
|
1371
|
+
visible: false,
|
|
1372
|
+
x: "auto",
|
|
1373
|
+
y: 54,
|
|
1374
|
+
position: "center",
|
|
1375
|
+
margin: 12,
|
|
1376
|
+
offsetX: 0,
|
|
1377
|
+
offsetY: 0,
|
|
1378
|
+
color: "#111827",
|
|
1379
|
+
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
1380
|
+
fontSize: 38,
|
|
1381
|
+
fontWeight: 750,
|
|
1382
|
+
fractionDigits: 0,
|
|
1383
|
+
prefix: "",
|
|
1384
|
+
suffix: "",
|
|
1385
|
+
locale: void 0,
|
|
1386
|
+
formatter: null
|
|
1387
|
+
},
|
|
1388
|
+
unit: {
|
|
1389
|
+
visible: false,
|
|
1390
|
+
text: "",
|
|
1391
|
+
x: "auto",
|
|
1392
|
+
y: 76,
|
|
1393
|
+
position: "bottom",
|
|
1394
|
+
margin: 12,
|
|
1395
|
+
offsetX: 0,
|
|
1396
|
+
offsetY: 0,
|
|
1397
|
+
color: "#6b7280",
|
|
1398
|
+
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
1399
|
+
fontSize: 12,
|
|
1400
|
+
fontWeight: 600
|
|
1401
|
+
}
|
|
1402
|
+
},
|
|
1403
|
+
animation: {
|
|
1404
|
+
enabled: true,
|
|
1405
|
+
duration: 850,
|
|
1406
|
+
easing: "easeOutCubic",
|
|
1407
|
+
animateOnInit: true,
|
|
1408
|
+
startFrom: "min",
|
|
1409
|
+
respectReducedMotion: true
|
|
1410
|
+
},
|
|
1411
|
+
effects: {
|
|
1412
|
+
jitter: {
|
|
1413
|
+
enabled: false,
|
|
1414
|
+
amplitude: 0.45,
|
|
1415
|
+
amplitudeUnit: "value",
|
|
1416
|
+
frequency: 4,
|
|
1417
|
+
smoothing: 0.86,
|
|
1418
|
+
whenIdleOnly: true
|
|
1419
|
+
}
|
|
1420
|
+
},
|
|
1421
|
+
accessibility: {
|
|
1422
|
+
label: "Gauge",
|
|
1423
|
+
description: "",
|
|
1424
|
+
valueText: null
|
|
1425
|
+
},
|
|
1426
|
+
performance: {
|
|
1427
|
+
pauseWhenHidden: true,
|
|
1428
|
+
pauseWhenOffscreen: true
|
|
1429
|
+
},
|
|
1430
|
+
responsive: true
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
// src/core/options.js
|
|
1434
|
+
var VALID_OVERFLOW = /* @__PURE__ */ new Set(["clamp", "expand", "allow"]);
|
|
1435
|
+
var VALID_POINTERS = /* @__PURE__ */ new Set(["needle", "line", "arrow", "spear"]);
|
|
1436
|
+
var VALID_POSITIONS = /* @__PURE__ */ new Set(["inside", "outside", "cross"]);
|
|
1437
|
+
var VALID_ROTATIONS = /* @__PURE__ */ new Set(["horizontal", "radial", "tangent"]);
|
|
1438
|
+
var VALID_LAYOUT_MODES = /* @__PURE__ */ new Set(["intrinsic", "contain"]);
|
|
1439
|
+
var VALID_FACE_FITS = /* @__PURE__ */ new Set(["geometry", "content"]);
|
|
1440
|
+
var VALID_FACE_SHAPES = /* @__PURE__ */ new Set(["panel", "circle"]);
|
|
1441
|
+
var VALID_ENDPOINT_MODES = /* @__PURE__ */ new Set(["auto", "both", "dedupe"]);
|
|
1442
|
+
var VALID_READOUT_POSITIONS = /* @__PURE__ */ new Set(["top", "center", "bottom"]);
|
|
1443
|
+
var VALID_GEOMETRY_MODES = /* @__PURE__ */ new Set(["radial", "linear", "tape"]);
|
|
1444
|
+
var VALID_LINEAR_TICK_SIDES = /* @__PURE__ */ new Set(["negative", "positive", "cross"]);
|
|
1445
|
+
var VALID_INDICATOR_TYPES = /* @__PURE__ */ new Set(["hairline", "marker", "carriage"]);
|
|
1446
|
+
var VALID_INDICATOR_POSITIONS = /* @__PURE__ */ new Set(["negative", "positive", "cross"]);
|
|
1447
|
+
var VALID_CAP_STACK_ORDERS = /* @__PURE__ */ new Set(["above", "below"]);
|
|
1448
|
+
var VALID_ORIENTATIONS = /* @__PURE__ */ new Set(["vertical", "horizontal"]);
|
|
1449
|
+
var VALID_LIGHT_COLORS = /* @__PURE__ */ new Set([
|
|
1450
|
+
"red",
|
|
1451
|
+
"green",
|
|
1452
|
+
"blue",
|
|
1453
|
+
"amber",
|
|
1454
|
+
"yellow",
|
|
1455
|
+
"orange",
|
|
1456
|
+
"white",
|
|
1457
|
+
"halogen",
|
|
1458
|
+
"cyan",
|
|
1459
|
+
"brown"
|
|
1460
|
+
]);
|
|
1461
|
+
var VALID_LIGHT_TRIGGER_MODES = /* @__PURE__ */ new Set(["manual", "below", "above", "between"]);
|
|
1462
|
+
var VALID_LIGHT_TRIGGER_SOURCES = /* @__PURE__ */ new Set(["target", "displayed", "pointer"]);
|
|
1463
|
+
function resolveGaugeOptions(userOptions, registry) {
|
|
1464
|
+
const requested = isPlainObject(userOptions) ? userOptions : {};
|
|
1465
|
+
const requestedType = String(requested.type || BASE_DEFAULTS.type);
|
|
1466
|
+
const typeDefinition = registry.get(requestedType);
|
|
1467
|
+
const fallbacks = deepMerge(BASE_DEFAULTS, typeDefinition.defaults);
|
|
1468
|
+
let options = deepMerge(fallbacks, requested);
|
|
1469
|
+
options.type = requestedType;
|
|
1470
|
+
options = restoreOptionShape(options, fallbacks);
|
|
1471
|
+
if (typeof typeDefinition.configure === "function") {
|
|
1472
|
+
const configured = typeDefinition.configure(options, {
|
|
1473
|
+
requested,
|
|
1474
|
+
typeDefaults: typeDefinition.defaults
|
|
1475
|
+
});
|
|
1476
|
+
if (isPlainObject(configured)) options = configured;
|
|
1477
|
+
}
|
|
1478
|
+
options = restoreOptionShape(options, fallbacks);
|
|
1479
|
+
options.min = finiteNumber(options.min, 0);
|
|
1480
|
+
options.max = finiteNumber(options.max, 100);
|
|
1481
|
+
if (options.max <= options.min) options.max = options.min + 1;
|
|
1482
|
+
options.value = finiteNumber(options.value, options.min);
|
|
1483
|
+
options.invert = options.invert === true;
|
|
1484
|
+
options.precision = Math.max(0, Math.min(12, Math.floor(finiteNumber(options.precision, 3))));
|
|
1485
|
+
options.overflow = VALID_OVERFLOW.has(options.overflow) ? options.overflow : "clamp";
|
|
1486
|
+
validateLayout(options.layout);
|
|
1487
|
+
validateGeometry(options.geometry);
|
|
1488
|
+
validateLinear(options.linear);
|
|
1489
|
+
validateFace(options.face);
|
|
1490
|
+
validateScale(options);
|
|
1491
|
+
validatePointer(options.pointer);
|
|
1492
|
+
validateIndicator(options.indicator, requested.indicator);
|
|
1493
|
+
validateCenterZero(options.centerZero, options);
|
|
1494
|
+
validateTape(options.tape);
|
|
1495
|
+
validateLight(options.light);
|
|
1496
|
+
validateLabels(options.labels);
|
|
1497
|
+
validateReadout(options.readout);
|
|
1498
|
+
validateAnimation(options.animation);
|
|
1499
|
+
validateJitter(options.effects.jitter);
|
|
1500
|
+
options.zones = normalizeZones(options.zones, options.min, options.max, options.track.width);
|
|
1501
|
+
return options;
|
|
1502
|
+
}
|
|
1503
|
+
function resolveMajorInterval(options) {
|
|
1504
|
+
const explicit = finiteNumber(options.scale.major.interval, 0);
|
|
1505
|
+
if (explicit > 0) return explicit;
|
|
1506
|
+
const divisions = Math.max(1, Math.floor(finiteNumber(options.scale.major.divisions, 5)));
|
|
1507
|
+
return niceInterval(options.max - options.min, divisions);
|
|
1508
|
+
}
|
|
1509
|
+
function mergeUserOptions(currentUserOptions, patch) {
|
|
1510
|
+
return deepMerge(currentUserOptions || {}, patch || {});
|
|
1511
|
+
}
|
|
1512
|
+
function validateLayout(layout) {
|
|
1513
|
+
layout.mode = VALID_LAYOUT_MODES.has(layout.mode) ? layout.mode : "intrinsic";
|
|
1514
|
+
layout.padding = Math.max(0, finiteNumber(layout.padding, 8));
|
|
1515
|
+
layout.includeGeometry = layout.includeGeometry !== false;
|
|
1516
|
+
}
|
|
1517
|
+
function validateGeometry(geometry2) {
|
|
1518
|
+
geometry2.mode = VALID_GEOMETRY_MODES.has(geometry2.mode) ? geometry2.mode : "radial";
|
|
1519
|
+
geometry2.width = Math.max(40, finiteNumber(geometry2.width, 320));
|
|
1520
|
+
geometry2.height = Math.max(40, finiteNumber(geometry2.height, 220));
|
|
1521
|
+
geometry2.centerX = finiteNumber(geometry2.centerX, geometry2.width / 2);
|
|
1522
|
+
geometry2.centerY = finiteNumber(geometry2.centerY, geometry2.height * 0.78);
|
|
1523
|
+
geometry2.radius = Math.max(1, finiteNumber(geometry2.radius, Math.min(geometry2.width, geometry2.height) * 0.4));
|
|
1524
|
+
geometry2.startAngle = finiteNumber(geometry2.startAngle, 180);
|
|
1525
|
+
geometry2.endAngle = finiteNumber(geometry2.endAngle, 360);
|
|
1526
|
+
if (Math.abs(geometry2.endAngle - geometry2.startAngle) < 1e-3) geometry2.endAngle = geometry2.startAngle + 180;
|
|
1527
|
+
if (Math.abs(geometry2.endAngle - geometry2.startAngle) > 360) {
|
|
1528
|
+
geometry2.endAngle = geometry2.startAngle + Math.sign(geometry2.endAngle - geometry2.startAngle) * 360;
|
|
1529
|
+
}
|
|
1530
|
+
geometry2.preserveAspectRatio = String(geometry2.preserveAspectRatio || "xMidYMid meet");
|
|
1531
|
+
geometry2.aspectRatio = geometry2.aspectRatio === null || geometry2.aspectRatio === void 0 ? null : Math.max(1e-6, finiteNumber(geometry2.aspectRatio, geometry2.width / geometry2.height));
|
|
1532
|
+
}
|
|
1533
|
+
function validateLinear(linear) {
|
|
1534
|
+
linear.originY = finiteNumber(linear.originY, 140);
|
|
1535
|
+
linear.startX = finiteNumber(linear.startX, 36);
|
|
1536
|
+
linear.startY = finiteNumber(linear.startY, 0);
|
|
1537
|
+
linear.endX = finiteNumber(linear.endX, 284);
|
|
1538
|
+
linear.endY = finiteNumber(linear.endY, linear.startY);
|
|
1539
|
+
if (Math.hypot(linear.endX - linear.startX, linear.endY - linear.startY) < 1e-3) {
|
|
1540
|
+
linear.endX = linear.startX + 1;
|
|
1541
|
+
linear.endY = linear.startY;
|
|
1542
|
+
}
|
|
1543
|
+
linear.tickSide = VALID_LINEAR_TICK_SIDES.has(linear.tickSide) ? linear.tickSide : "negative";
|
|
1544
|
+
linear.labelOffset = finiteNumber(linear.labelOffset, 34);
|
|
1545
|
+
linear.zoneOffset = finiteNumber(linear.zoneOffset, -12);
|
|
1546
|
+
}
|
|
1547
|
+
function validateFace(face) {
|
|
1548
|
+
face.shape = VALID_FACE_SHAPES.has(face.shape) ? face.shape : "panel";
|
|
1549
|
+
face.fit = VALID_FACE_FITS.has(face.fit) ? face.fit : "geometry";
|
|
1550
|
+
face.contentPadding = Math.max(0, finiteNumber(face.contentPadding, 8));
|
|
1551
|
+
face.minWidth = face.minWidth === null || face.minWidth === void 0 ? null : Math.max(1, finiteNumber(face.minWidth, 1));
|
|
1552
|
+
face.minHeight = face.minHeight === null || face.minHeight === void 0 ? null : Math.max(1, finiteNumber(face.minHeight, 1));
|
|
1553
|
+
face.clipContent = face.clipContent === true;
|
|
1554
|
+
face.x = finiteNumber(face.x, 6);
|
|
1555
|
+
face.y = finiteNumber(face.y, 6);
|
|
1556
|
+
face.width = Math.max(1, finiteNumber(face.width, 308));
|
|
1557
|
+
face.height = Math.max(1, finiteNumber(face.height, 208));
|
|
1558
|
+
face.cornerRadius = Math.max(0, finiteNumber(face.cornerRadius, 18));
|
|
1559
|
+
face.strokeWidth = Math.max(0, finiteNumber(face.strokeWidth, 2));
|
|
1560
|
+
face.innerStrokeWidth = Math.max(0, finiteNumber(face.innerStrokeWidth, 1));
|
|
1561
|
+
}
|
|
1562
|
+
function validateScale(options) {
|
|
1563
|
+
options.scale.endpointMode = VALID_ENDPOINT_MODES.has(options.scale.endpointMode) ? options.scale.endpointMode : "auto";
|
|
1564
|
+
options.scale.radius = Math.max(1, finiteNumber(options.scale.radius, options.geometry.radius));
|
|
1565
|
+
options.scale.position = VALID_POSITIONS.has(options.scale.position) ? options.scale.position : "inside";
|
|
1566
|
+
options.scale.major.divisions = Math.max(1, Math.floor(finiteNumber(options.scale.major.divisions, 5)));
|
|
1567
|
+
options.scale.major.length = Math.max(0, finiteNumber(options.scale.major.length, 13));
|
|
1568
|
+
options.scale.major.width = Math.max(0, finiteNumber(options.scale.major.width, 2));
|
|
1569
|
+
options.scale.minor.subdivisions = Math.max(0, Math.floor(finiteNumber(options.scale.minor.subdivisions, 4)));
|
|
1570
|
+
options.scale.minor.length = Math.max(0, finiteNumber(options.scale.minor.length, 7));
|
|
1571
|
+
options.scale.minor.width = Math.max(0, finiteNumber(options.scale.minor.width, 1));
|
|
1572
|
+
}
|
|
1573
|
+
function validatePointer(pointer) {
|
|
1574
|
+
pointer.type = VALID_POINTERS.has(pointer.type) ? pointer.type : "needle";
|
|
1575
|
+
pointer.length = Math.max(0, finiteNumber(pointer.length, 94));
|
|
1576
|
+
pointer.shaftLength = pointer.shaftLength === null || pointer.shaftLength === void 0 ? null : Math.min(pointer.length, Math.max(0, finiteNumber(pointer.shaftLength, pointer.length)));
|
|
1577
|
+
pointer.tailLength = Math.max(0, finiteNumber(pointer.tailLength, 13));
|
|
1578
|
+
pointer.width = Math.max(0.5, finiteNumber(pointer.width, 8));
|
|
1579
|
+
pointer.strokeWidth = Math.max(0, finiteNumber(pointer.strokeWidth, 0));
|
|
1580
|
+
pointer.cap.radius = Math.max(0, finiteNumber(pointer.cap.radius, 8));
|
|
1581
|
+
pointer.cap.strokeWidth = Math.max(0, finiteNumber(pointer.cap.strokeWidth, 2));
|
|
1582
|
+
pointer.cap.stackOrder = VALID_CAP_STACK_ORDERS.has(pointer.cap.stackOrder) ? pointer.cap.stackOrder : "above";
|
|
1583
|
+
}
|
|
1584
|
+
function validateIndicator(indicator, requestedIndicator = {}) {
|
|
1585
|
+
indicator.visible = indicator.visible === true;
|
|
1586
|
+
indicator.type = VALID_INDICATOR_TYPES.has(indicator.type) ? indicator.type : "hairline";
|
|
1587
|
+
indicator.position = VALID_INDICATOR_POSITIONS.has(indicator.position) ? indicator.position : "cross";
|
|
1588
|
+
indicator.length = Math.max(0, finiteNumber(indicator.length, 76));
|
|
1589
|
+
const explicitCenterOffset = isPlainObject(requestedIndicator) && Object.prototype.hasOwnProperty.call(requestedIndicator, "centerOffset");
|
|
1590
|
+
const explicitLegacyOffset = isPlainObject(requestedIndicator) && Object.prototype.hasOwnProperty.call(requestedIndicator, "offset");
|
|
1591
|
+
if (explicitCenterOffset) indicator.centerOffset = finiteNumber(requestedIndicator.centerOffset, 0);
|
|
1592
|
+
else if (explicitLegacyOffset) indicator.centerOffset = -finiteNumber(requestedIndicator.offset, 0);
|
|
1593
|
+
else if (indicator.centerOffset === null || indicator.centerOffset === void 0) {
|
|
1594
|
+
indicator.centerOffset = -finiteNumber(indicator.offset, 0);
|
|
1595
|
+
} else indicator.centerOffset = finiteNumber(indicator.centerOffset, 0);
|
|
1596
|
+
indicator.offset = -indicator.centerOffset;
|
|
1597
|
+
indicator.width = Math.max(0.5, finiteNumber(indicator.width, 2));
|
|
1598
|
+
indicator.strokeWidth = Math.max(0, finiteNumber(indicator.strokeWidth, 0));
|
|
1599
|
+
indicator.opacity = Math.min(1, Math.max(0, finiteNumber(indicator.opacity, 1)));
|
|
1600
|
+
indicator.markerSize = Math.max(1, finiteNumber(indicator.markerSize, 8));
|
|
1601
|
+
indicator.carriageWidth = Math.max(1, finiteNumber(indicator.carriageWidth, 14));
|
|
1602
|
+
indicator.carriageRadius = Math.max(0, finiteNumber(indicator.carriageRadius, 4));
|
|
1603
|
+
indicator.cap.radius = Math.max(0, finiteNumber(indicator.cap.radius, 4));
|
|
1604
|
+
indicator.cap.strokeWidth = Math.max(0, finiteNumber(indicator.cap.strokeWidth, 1));
|
|
1605
|
+
indicator.cap.stackOrder = VALID_CAP_STACK_ORDERS.has(indicator.cap.stackOrder) ? indicator.cap.stackOrder : "above";
|
|
1606
|
+
}
|
|
1607
|
+
function validateCenterZero(centerZero, options) {
|
|
1608
|
+
centerZero.zeroValue = finiteNumber(centerZero.zeroValue, 0);
|
|
1609
|
+
centerZero.symmetric = centerZero.symmetric === true;
|
|
1610
|
+
if (centerZero.symmetric) {
|
|
1611
|
+
const magnitude = Math.max(
|
|
1612
|
+
Math.abs(options.min - centerZero.zeroValue),
|
|
1613
|
+
Math.abs(options.max - centerZero.zeroValue),
|
|
1614
|
+
1
|
|
1615
|
+
);
|
|
1616
|
+
options.min = centerZero.zeroValue - magnitude;
|
|
1617
|
+
options.max = centerZero.zeroValue + magnitude;
|
|
1618
|
+
}
|
|
1619
|
+
centerZero.marker.visible = centerZero.marker.visible === true;
|
|
1620
|
+
centerZero.marker.radius = centerZero.marker.radius === null || centerZero.marker.radius === void 0 ? null : Math.max(0, finiteNumber(centerZero.marker.radius, options.scale.radius));
|
|
1621
|
+
centerZero.marker.position = VALID_INDICATOR_POSITIONS.has(centerZero.marker.position) ? centerZero.marker.position : "cross";
|
|
1622
|
+
centerZero.marker.centerOffset = finiteNumber(centerZero.marker.centerOffset, 0);
|
|
1623
|
+
centerZero.marker.length = Math.max(0, finiteNumber(centerZero.marker.length, 20));
|
|
1624
|
+
centerZero.marker.width = Math.max(0.5, finiteNumber(centerZero.marker.width, 3));
|
|
1625
|
+
centerZero.marker.opacity = Math.min(1, Math.max(0, finiteNumber(centerZero.marker.opacity, 1)));
|
|
1626
|
+
centerZero.marker.lineCap = String(centerZero.marker.lineCap || "round");
|
|
1627
|
+
}
|
|
1628
|
+
function validateTape(tape) {
|
|
1629
|
+
tape.orientation = VALID_ORIENTATIONS.has(tape.orientation) ? tape.orientation : "vertical";
|
|
1630
|
+
tape.x = finiteNumber(tape.x, 64);
|
|
1631
|
+
tape.y = finiteNumber(tape.y, 58);
|
|
1632
|
+
tape.width = Math.max(20, finiteNumber(tape.width, 192));
|
|
1633
|
+
tape.height = Math.max(20, finiteNumber(tape.height, 210));
|
|
1634
|
+
tape.cornerRadius = Math.max(0, finiteNumber(tape.cornerRadius, 12));
|
|
1635
|
+
tape.windowStrokeWidth = Math.max(0, finiteNumber(tape.windowStrokeWidth, 2));
|
|
1636
|
+
tape.windowInnerStrokeWidth = Math.max(0, finiteNumber(tape.windowInnerStrokeWidth, 1));
|
|
1637
|
+
tape.minorTickOpacity = Math.min(1, Math.max(0, finiteNumber(tape.minorTickOpacity, 0.85)));
|
|
1638
|
+
tape.majorTickWidth = Math.max(0.5, finiteNumber(tape.majorTickWidth, 2));
|
|
1639
|
+
tape.minorTickWidth = Math.max(0.5, finiteNumber(tape.minorTickWidth, 1));
|
|
1640
|
+
tape.majorTickLength = Math.max(0, finiteNumber(tape.majorTickLength, 24));
|
|
1641
|
+
tape.minorTickLength = Math.max(0, finiteNumber(tape.minorTickLength, 12));
|
|
1642
|
+
tape.tickInset = finiteNumber(tape.tickInset, 64);
|
|
1643
|
+
tape.labelOffset = finiteNumber(tape.labelOffset, 10);
|
|
1644
|
+
tape.interval = tape.interval === null || tape.interval === void 0 ? null : Math.max(1e-6, finiteNumber(tape.interval, 1));
|
|
1645
|
+
tape.minorSubdivisions = Math.max(0, Math.floor(finiteNumber(tape.minorSubdivisions, 4)));
|
|
1646
|
+
tape.majorSpacing = Math.max(2, finiteNumber(tape.majorSpacing, 52));
|
|
1647
|
+
tape.indicatorWidth = Math.max(0.5, finiteNumber(tape.indicatorWidth, 2.5));
|
|
1648
|
+
const fadeAxisSize = tape.orientation === "horizontal" ? tape.width : tape.height;
|
|
1649
|
+
tape.fadeSize = Math.min(fadeAxisSize / 2, Math.max(0, finiteNumber(tape.fadeSize, 32)));
|
|
1650
|
+
tape.colorByZone = tape.colorByZone === true;
|
|
1651
|
+
}
|
|
1652
|
+
function validateLight(light) {
|
|
1653
|
+
light.visible = light.visible === true;
|
|
1654
|
+
light.x = finiteNumber(light.x, 160);
|
|
1655
|
+
light.y = finiteNumber(light.y, 110);
|
|
1656
|
+
light.radius = Math.max(1, finiteNumber(light.radius, 8));
|
|
1657
|
+
light.color = VALID_LIGHT_COLORS.has(light.color) ? light.color : "amber";
|
|
1658
|
+
light.customColor = light.customColor === null || light.customColor === void 0 ? "" : String(light.customColor).trim();
|
|
1659
|
+
light.offColor = normalizeColor(light.offColor, "#f3ead6");
|
|
1660
|
+
light.offEdgeColor = normalizeColor(light.offEdgeColor, "#817768");
|
|
1661
|
+
light.bezelColor = normalizeColor(light.bezelColor, "#4b5563");
|
|
1662
|
+
light.bezelHighlightColor = normalizeColor(light.bezelHighlightColor, "#f8fafc");
|
|
1663
|
+
light.opacity = Math.min(1, Math.max(0, finiteNumber(light.opacity, 1)));
|
|
1664
|
+
light.on = light.on === true;
|
|
1665
|
+
light.glow = light.glow !== false;
|
|
1666
|
+
light.pulse = light.pulse !== false;
|
|
1667
|
+
light.pulseInterval = Math.max(400, finiteNumber(light.pulseInterval, 2600));
|
|
1668
|
+
light.blink = light.blink === true;
|
|
1669
|
+
light.blinkInterval = Math.max(100, finiteNumber(light.blinkInterval, 1e3));
|
|
1670
|
+
light.trigger = isPlainObject(light.trigger) ? light.trigger : {};
|
|
1671
|
+
light.trigger.mode = VALID_LIGHT_TRIGGER_MODES.has(light.trigger.mode) ? light.trigger.mode : "manual";
|
|
1672
|
+
light.trigger.source = VALID_LIGHT_TRIGGER_SOURCES.has(light.trigger.source) ? light.trigger.source : "target";
|
|
1673
|
+
light.trigger.value = finiteNumber(light.trigger.value, 0);
|
|
1674
|
+
light.trigger.min = finiteNumber(light.trigger.min, light.trigger.value);
|
|
1675
|
+
light.trigger.max = finiteNumber(light.trigger.max, light.trigger.value);
|
|
1676
|
+
if (light.trigger.max < light.trigger.min) {
|
|
1677
|
+
[light.trigger.min, light.trigger.max] = [light.trigger.max, light.trigger.min];
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
function normalizeColor(value, fallback) {
|
|
1681
|
+
if (value === null || value === void 0) return fallback;
|
|
1682
|
+
const normalized = String(value).trim();
|
|
1683
|
+
return normalized || fallback;
|
|
1684
|
+
}
|
|
1685
|
+
function validateLabels(labels) {
|
|
1686
|
+
labels.radius = Math.max(0, finiteNumber(labels.radius, 84));
|
|
1687
|
+
labels.fontSize = Math.max(1, finiteNumber(labels.fontSize, 11));
|
|
1688
|
+
labels.fractionDigits = Math.max(0, Math.min(12, Math.floor(finiteNumber(labels.fractionDigits, 0))));
|
|
1689
|
+
labels.rotation = VALID_ROTATIONS.has(labels.rotation) ? labels.rotation : "horizontal";
|
|
1690
|
+
}
|
|
1691
|
+
function validateReadout(readout) {
|
|
1692
|
+
readout.visible = readout.visible !== false;
|
|
1693
|
+
validateReadoutPart(readout.title, 14);
|
|
1694
|
+
validateReadoutPart(readout.value, 38);
|
|
1695
|
+
validateReadoutPart(readout.unit, 12);
|
|
1696
|
+
readout.value.fractionDigits = Math.max(
|
|
1697
|
+
0,
|
|
1698
|
+
Math.min(12, Math.floor(finiteNumber(readout.value.fractionDigits, 0)))
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1701
|
+
function validateReadoutPart(part, defaultFontSize) {
|
|
1702
|
+
part.visible = part.visible === true;
|
|
1703
|
+
part.x = part.x === "auto" ? "auto" : finiteNumber(part.x, 160);
|
|
1704
|
+
part.y = part.y === "auto" ? "auto" : finiteNumber(part.y, 0);
|
|
1705
|
+
part.position = VALID_READOUT_POSITIONS.has(part.position) ? part.position : "top";
|
|
1706
|
+
part.margin = Math.max(0, finiteNumber(part.margin, 12));
|
|
1707
|
+
part.offsetX = finiteNumber(part.offsetX, 0);
|
|
1708
|
+
part.offsetY = finiteNumber(part.offsetY, 0);
|
|
1709
|
+
part.fontSize = Math.max(1, finiteNumber(part.fontSize, defaultFontSize));
|
|
1710
|
+
part.letterSpacing = finiteNumber(part.letterSpacing, 0);
|
|
1711
|
+
}
|
|
1712
|
+
function validateAnimation(animation) {
|
|
1713
|
+
animation.duration = Math.max(0, finiteNumber(animation.duration, 850));
|
|
1714
|
+
if (typeof animation.startFrom !== "number" && !["min", "max", "value"].includes(animation.startFrom)) {
|
|
1715
|
+
animation.startFrom = "min";
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
function validateJitter(jitter) {
|
|
1719
|
+
jitter.amplitude = Math.max(0, finiteNumber(jitter.amplitude, 0.45));
|
|
1720
|
+
jitter.amplitudeUnit = jitter.amplitudeUnit === "percent" ? "percent" : "value";
|
|
1721
|
+
jitter.frequency = Math.max(0.1, finiteNumber(jitter.frequency, 4));
|
|
1722
|
+
jitter.smoothing = Math.min(0.999, Math.max(0, finiteNumber(jitter.smoothing, 0.86)));
|
|
1723
|
+
}
|
|
1724
|
+
function normalizeZones(zones, min, max, defaultWidth) {
|
|
1725
|
+
if (!Array.isArray(zones)) return [];
|
|
1726
|
+
return zones.filter(isPlainObject).map((zone) => {
|
|
1727
|
+
var _a, _b;
|
|
1728
|
+
const width = zone.width === void 0 ? void 0 : Math.max(0, finiteNumber(zone.width, 0));
|
|
1729
|
+
const taper = normalizeZoneTaper(zone.taper, width != null ? width : defaultWidth);
|
|
1730
|
+
return {
|
|
1731
|
+
...zone,
|
|
1732
|
+
min: Math.max(min, finiteNumber(zone.min, min)),
|
|
1733
|
+
max: Math.min(max, finiteNumber(zone.max, max)),
|
|
1734
|
+
color: (_b = (_a = zone.color) != null ? _a : zone.strokeStyle) != null ? _b : "transparent",
|
|
1735
|
+
width,
|
|
1736
|
+
taper,
|
|
1737
|
+
radius: zone.radius === void 0 ? void 0 : Math.max(0, finiteNumber(zone.radius, 0)),
|
|
1738
|
+
offset: zone.offset === void 0 ? void 0 : finiteNumber(zone.offset, 0),
|
|
1739
|
+
opacity: zone.opacity === void 0 ? 1 : Math.min(1, Math.max(0, finiteNumber(zone.opacity, 1)))
|
|
1740
|
+
};
|
|
1741
|
+
}).filter((zone) => zone.max > zone.min);
|
|
1742
|
+
}
|
|
1743
|
+
function restoreOptionShape(options, fallbacks) {
|
|
1744
|
+
if (!isPlainObject(options)) return cloneValue(fallbacks);
|
|
1745
|
+
for (const [key, fallback] of Object.entries(fallbacks)) {
|
|
1746
|
+
if (!isPlainObject(fallback)) continue;
|
|
1747
|
+
options[key] = restoreOptionShape(options[key], fallback);
|
|
1748
|
+
}
|
|
1749
|
+
return options;
|
|
1750
|
+
}
|
|
1751
|
+
function normalizeZoneTaper(taper, fallbackWidth) {
|
|
1752
|
+
var _a;
|
|
1753
|
+
if (!isPlainObject(taper)) return void 0;
|
|
1754
|
+
const startWidth = taper.startWidth === void 0 ? fallbackWidth : Math.max(0, finiteNumber(taper.startWidth, fallbackWidth != null ? fallbackWidth : 0));
|
|
1755
|
+
const endWidth = taper.endWidth === void 0 ? fallbackWidth : Math.max(0, finiteNumber(taper.endWidth, (_a = fallbackWidth != null ? fallbackWidth : startWidth) != null ? _a : 0));
|
|
1756
|
+
return {
|
|
1757
|
+
startWidth,
|
|
1758
|
+
endWidth,
|
|
1759
|
+
segments: Math.max(2, Math.min(256, Math.floor(finiteNumber(taper.segments, 48))))
|
|
1760
|
+
};
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
// src/core/gauge.js
|
|
1764
|
+
var Gauge = class {
|
|
1765
|
+
constructor(target, userOptions, registry, lifecycle = {}) {
|
|
1766
|
+
this.host = resolveElement(target);
|
|
1767
|
+
this.registry = registry;
|
|
1768
|
+
this._onDestroy = typeof lifecycle.onDestroy === "function" ? lifecycle.onDestroy : null;
|
|
1769
|
+
this.userOptions = mergeUserOptions({}, userOptions || {});
|
|
1770
|
+
this.options = resolveGaugeOptions(this.userOptions, registry);
|
|
1771
|
+
this.renderer = new SvgRenderer(this.host, this.options);
|
|
1772
|
+
this._destroyed = false;
|
|
1773
|
+
this._paused = false;
|
|
1774
|
+
this._pausedAt = 0;
|
|
1775
|
+
this._lifecycleSuspendedAt = 0;
|
|
1776
|
+
this._onscreen = true;
|
|
1777
|
+
this._frameHandle = null;
|
|
1778
|
+
this._animation = null;
|
|
1779
|
+
this._targetValue = this._normalizeIncomingValue(this.options.value);
|
|
1780
|
+
this._baseValue = this._targetValue;
|
|
1781
|
+
this._displayValue = this._targetValue;
|
|
1782
|
+
this._pointerValue = this._targetValue;
|
|
1783
|
+
this._jitterOffset = 0;
|
|
1784
|
+
this._jitterTarget = 0;
|
|
1785
|
+
this._lastJitterTargetAt = 0;
|
|
1786
|
+
this._lastFrameAt = 0;
|
|
1787
|
+
this._visibilityHandler = null;
|
|
1788
|
+
this._intersectionObserver = null;
|
|
1789
|
+
this._syncLifecycleObservers();
|
|
1790
|
+
this.render();
|
|
1791
|
+
this._initializeValue();
|
|
1792
|
+
}
|
|
1793
|
+
/** Fully rebuilds static layers and dynamic controllers. */
|
|
1794
|
+
render() {
|
|
1795
|
+
this._assertAlive();
|
|
1796
|
+
this.renderer.options = this.options;
|
|
1797
|
+
this.renderer.begin();
|
|
1798
|
+
const definition = this.registry.get(this.options.type);
|
|
1799
|
+
const context = {
|
|
1800
|
+
gauge: this,
|
|
1801
|
+
options: this.options,
|
|
1802
|
+
renderer: this.renderer,
|
|
1803
|
+
registry: this.registry
|
|
1804
|
+
};
|
|
1805
|
+
for (const layer of definition.layers) {
|
|
1806
|
+
const controller = layer.render(context);
|
|
1807
|
+
this.renderer.addController(controller);
|
|
1808
|
+
}
|
|
1809
|
+
this._updateDynamic(this._displayValue, true, this._pointerValue);
|
|
1810
|
+
this.renderer.finish();
|
|
1811
|
+
dispatchGaugeEvent(this.host, "render", {
|
|
1812
|
+
gauge: this,
|
|
1813
|
+
type: this.options.type,
|
|
1814
|
+
value: this._targetValue
|
|
1815
|
+
});
|
|
1816
|
+
return this;
|
|
1817
|
+
}
|
|
1818
|
+
/** Sets a new gauge value. */
|
|
1819
|
+
setValue(value, updateOptions = {}) {
|
|
1820
|
+
this._assertAlive();
|
|
1821
|
+
const normalized = this._normalizeIncomingValue(value);
|
|
1822
|
+
const duration = Math.max(0, finiteNumber(updateOptions.duration, this.options.animation.duration));
|
|
1823
|
+
const animate = updateOptions.animate !== false && this.options.animation.enabled && duration > 0 && !this._prefersReducedMotion();
|
|
1824
|
+
this._targetValue = normalized;
|
|
1825
|
+
this.options.value = normalized;
|
|
1826
|
+
if (!animate) {
|
|
1827
|
+
this._animation = null;
|
|
1828
|
+
this._baseValue = normalized;
|
|
1829
|
+
this._displayValue = normalized;
|
|
1830
|
+
this._pointerValue = normalized;
|
|
1831
|
+
this._jitterOffset = 0;
|
|
1832
|
+
this._updateDynamic(normalized, true, normalized);
|
|
1833
|
+
this._ensureLoop();
|
|
1834
|
+
this._emitChange();
|
|
1835
|
+
return this;
|
|
1836
|
+
}
|
|
1837
|
+
const easing = resolveEasing(updateOptions.easing || this.options.animation.easing);
|
|
1838
|
+
const now = performanceNow();
|
|
1839
|
+
this._animation = {
|
|
1840
|
+
from: this._baseValue,
|
|
1841
|
+
to: normalized,
|
|
1842
|
+
startedAt: now,
|
|
1843
|
+
duration,
|
|
1844
|
+
easing
|
|
1845
|
+
};
|
|
1846
|
+
dispatchGaugeEvent(this.host, "animationstart", {
|
|
1847
|
+
gauge: this,
|
|
1848
|
+
from: this._animation.from,
|
|
1849
|
+
to: normalized,
|
|
1850
|
+
duration
|
|
1851
|
+
});
|
|
1852
|
+
this._ensureLoop();
|
|
1853
|
+
this._emitChange();
|
|
1854
|
+
return this;
|
|
1855
|
+
}
|
|
1856
|
+
/** Gauge.js-compatible convenience alias. */
|
|
1857
|
+
set(value, updateOptions = {}) {
|
|
1858
|
+
return this.setValue(value, updateOptions);
|
|
1859
|
+
}
|
|
1860
|
+
/** Updates options by recursively merging a partial patch into the current user options. */
|
|
1861
|
+
updateOptions(patch, behavior = {}) {
|
|
1862
|
+
this._assertAlive();
|
|
1863
|
+
const requested = patch || {};
|
|
1864
|
+
return this._applyUserOptions(
|
|
1865
|
+
mergeUserOptions(this.userOptions, requested),
|
|
1866
|
+
Object.prototype.hasOwnProperty.call(requested, "value"),
|
|
1867
|
+
behavior
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
/** Replaces the complete user-option object while preserving the current value unless supplied. */
|
|
1871
|
+
replaceOptions(options = {}, behavior = {}) {
|
|
1872
|
+
this._assertAlive();
|
|
1873
|
+
const requested = options || {};
|
|
1874
|
+
return this._applyUserOptions(
|
|
1875
|
+
mergeUserOptions({}, requested),
|
|
1876
|
+
Object.prototype.hasOwnProperty.call(requested, "value"),
|
|
1877
|
+
behavior
|
|
1878
|
+
);
|
|
1879
|
+
}
|
|
1880
|
+
/** Gauge.js-style alias for updateOptions. */
|
|
1881
|
+
setOptions(patch, behavior = {}) {
|
|
1882
|
+
return this.updateOptions(patch, behavior);
|
|
1883
|
+
}
|
|
1884
|
+
_applyUserOptions(nextUserOptions, hadExplicitValue, behavior) {
|
|
1885
|
+
const previousType = this.options.type;
|
|
1886
|
+
this.userOptions = nextUserOptions;
|
|
1887
|
+
this.options = resolveGaugeOptions(this.userOptions, this.registry);
|
|
1888
|
+
this.renderer.options = this.options;
|
|
1889
|
+
this._syncLifecycleObservers();
|
|
1890
|
+
const value = hadExplicitValue ? this.options.value : this._targetValue;
|
|
1891
|
+
this._targetValue = this._normalizeIncomingValue(value);
|
|
1892
|
+
if (hadExplicitValue && !behavior.animateToValue) {
|
|
1893
|
+
this._animation = null;
|
|
1894
|
+
this._baseValue = this._targetValue;
|
|
1895
|
+
this._displayValue = this._targetValue;
|
|
1896
|
+
this._pointerValue = this._targetValue;
|
|
1897
|
+
this._jitterOffset = 0;
|
|
1898
|
+
} else {
|
|
1899
|
+
this._baseValue = this._normalizeIncomingValue(this._baseValue);
|
|
1900
|
+
this._displayValue = this._normalizeIncomingValue(this._displayValue);
|
|
1901
|
+
this._pointerValue = this._displayClamp(this._baseValue + this._jitterOffset);
|
|
1902
|
+
}
|
|
1903
|
+
this.options.value = this._targetValue;
|
|
1904
|
+
this.render();
|
|
1905
|
+
if (behavior.animateToValue) this.setValue(this._targetValue, { animate: true });
|
|
1906
|
+
else this._updateDynamic(this._displayValue, true, this._pointerValue);
|
|
1907
|
+
if (previousType !== this.options.type) {
|
|
1908
|
+
dispatchGaugeEvent(this.host, "typechange", {
|
|
1909
|
+
gauge: this,
|
|
1910
|
+
previousType,
|
|
1911
|
+
type: this.options.type
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
this._ensureLoop();
|
|
1915
|
+
return this;
|
|
1916
|
+
}
|
|
1917
|
+
/** Returns the last requested stable value. */
|
|
1918
|
+
getValue() {
|
|
1919
|
+
return this._targetValue;
|
|
1920
|
+
}
|
|
1921
|
+
/** Returns the currently rendered base value; visual pointer jitter is excluded. */
|
|
1922
|
+
getDisplayedValue() {
|
|
1923
|
+
return this._displayValue;
|
|
1924
|
+
}
|
|
1925
|
+
/** Temporarily stops animation and jitter work. */
|
|
1926
|
+
pause() {
|
|
1927
|
+
if (this._destroyed || this._paused) return this;
|
|
1928
|
+
this._paused = true;
|
|
1929
|
+
this._pausedAt = performanceNow();
|
|
1930
|
+
cancelFrame(this._frameHandle);
|
|
1931
|
+
this._frameHandle = null;
|
|
1932
|
+
dispatchGaugeEvent(this.host, "pause", { gauge: this });
|
|
1933
|
+
return this;
|
|
1934
|
+
}
|
|
1935
|
+
/** Resumes pending animation and jitter work. */
|
|
1936
|
+
resume() {
|
|
1937
|
+
if (this._destroyed || !this._paused) return this;
|
|
1938
|
+
const now = performanceNow();
|
|
1939
|
+
const suspensionStartedAt = this._lifecycleSuspendedAt > 0 ? Math.min(this._pausedAt, this._lifecycleSuspendedAt) : this._pausedAt;
|
|
1940
|
+
this._paused = false;
|
|
1941
|
+
if (this._animation && suspensionStartedAt > 0) {
|
|
1942
|
+
this._animation.startedAt += Math.max(0, now - suspensionStartedAt);
|
|
1943
|
+
}
|
|
1944
|
+
this._pausedAt = 0;
|
|
1945
|
+
this._lifecycleSuspendedAt = this._isLifecycleBlocked() ? now : 0;
|
|
1946
|
+
this._lastFrameAt = 0;
|
|
1947
|
+
this._ensureLoop();
|
|
1948
|
+
dispatchGaugeEvent(this.host, "resume", { gauge: this });
|
|
1949
|
+
return this;
|
|
1950
|
+
}
|
|
1951
|
+
/** Re-renders after custom CSS variables, fonts, or plugin state changed. */
|
|
1952
|
+
refresh() {
|
|
1953
|
+
return this.render();
|
|
1954
|
+
}
|
|
1955
|
+
/** Releases frames, observers, listeners, and generated DOM. */
|
|
1956
|
+
destroy() {
|
|
1957
|
+
if (this._destroyed) return;
|
|
1958
|
+
this._destroyed = true;
|
|
1959
|
+
cancelFrame(this._frameHandle);
|
|
1960
|
+
this._frameHandle = null;
|
|
1961
|
+
this._animation = null;
|
|
1962
|
+
this._removeLifecycleObservers();
|
|
1963
|
+
this.renderer.destroy();
|
|
1964
|
+
const onDestroy = this._onDestroy;
|
|
1965
|
+
this._onDestroy = null;
|
|
1966
|
+
if (onDestroy) onDestroy(this);
|
|
1967
|
+
dispatchGaugeEvent(this.host, "destroy", { gauge: this });
|
|
1968
|
+
}
|
|
1969
|
+
_initializeValue() {
|
|
1970
|
+
const animation = this.options.animation;
|
|
1971
|
+
if (!animation.enabled || !animation.animateOnInit || animation.duration <= 0 || this._prefersReducedMotion()) {
|
|
1972
|
+
this._baseValue = this._targetValue;
|
|
1973
|
+
this._displayValue = this._targetValue;
|
|
1974
|
+
this._pointerValue = this._targetValue;
|
|
1975
|
+
this._updateDynamic(this._targetValue, true, this._targetValue);
|
|
1976
|
+
this._ensureLoop();
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
const start = this._resolveInitialValue(animation.startFrom);
|
|
1980
|
+
if (Object.is(start, this._targetValue)) {
|
|
1981
|
+
this._updateDynamic(this._targetValue, true);
|
|
1982
|
+
this._ensureLoop();
|
|
1983
|
+
return;
|
|
1984
|
+
}
|
|
1985
|
+
const target = this._targetValue;
|
|
1986
|
+
this._baseValue = start;
|
|
1987
|
+
this._displayValue = start;
|
|
1988
|
+
this._pointerValue = start;
|
|
1989
|
+
this._updateDynamic(start, true, start);
|
|
1990
|
+
this.setValue(target, { animate: true });
|
|
1991
|
+
}
|
|
1992
|
+
_resolveInitialValue(startFrom) {
|
|
1993
|
+
if (typeof startFrom === "number") return this._normalizeIncomingValue(startFrom);
|
|
1994
|
+
if (startFrom === "max") return this.options.max;
|
|
1995
|
+
if (startFrom === "value") return this._targetValue;
|
|
1996
|
+
return this.options.min;
|
|
1997
|
+
}
|
|
1998
|
+
_normalizeIncomingValue(value) {
|
|
1999
|
+
const number = finiteNumber(value, this.options.min);
|
|
2000
|
+
if (this.options.overflow === "allow") return number;
|
|
2001
|
+
if (this.options.overflow === "expand") {
|
|
2002
|
+
let changed = false;
|
|
2003
|
+
if (number < this.options.min) {
|
|
2004
|
+
this.userOptions.min = number;
|
|
2005
|
+
changed = true;
|
|
2006
|
+
}
|
|
2007
|
+
if (number > this.options.max) {
|
|
2008
|
+
this.userOptions.max = number;
|
|
2009
|
+
changed = true;
|
|
2010
|
+
}
|
|
2011
|
+
if (changed) {
|
|
2012
|
+
this.options = resolveGaugeOptions(this.userOptions, this.registry);
|
|
2013
|
+
if (this.renderer) {
|
|
2014
|
+
this.renderer.options = this.options;
|
|
2015
|
+
if (this.renderer.svg) this.render();
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
return number;
|
|
2019
|
+
}
|
|
2020
|
+
return clamp(number, this.options.min, this.options.max);
|
|
2021
|
+
}
|
|
2022
|
+
_ensureLoop() {
|
|
2023
|
+
if (this._destroyed || this._paused || this._frameHandle !== null) return;
|
|
2024
|
+
if (!this._shouldRun()) return;
|
|
2025
|
+
this._frameHandle = requestFrame((timestamp) => this._frame(timestamp));
|
|
2026
|
+
}
|
|
2027
|
+
_frame(timestamp) {
|
|
2028
|
+
this._frameHandle = null;
|
|
2029
|
+
if (this._destroyed || this._paused || !this._shouldRun()) return;
|
|
2030
|
+
const now = Number.isFinite(timestamp) ? timestamp : performanceNow();
|
|
2031
|
+
const delta = this._lastFrameAt > 0 ? Math.min(100, Math.max(0, now - this._lastFrameAt)) : 16.667;
|
|
2032
|
+
this._lastFrameAt = now;
|
|
2033
|
+
let animationCompleted = false;
|
|
2034
|
+
if (this._animation) {
|
|
2035
|
+
const elapsed = now - this._animation.startedAt;
|
|
2036
|
+
const progress = this._animation.duration <= 0 ? 1 : clamp(elapsed / this._animation.duration, 0, 1);
|
|
2037
|
+
const eased = this._animation.easing(progress);
|
|
2038
|
+
this._baseValue = this._animation.from + (this._animation.to - this._animation.from) * eased;
|
|
2039
|
+
if (progress >= 1) {
|
|
2040
|
+
const completed = this._animation;
|
|
2041
|
+
this._baseValue = completed.to;
|
|
2042
|
+
this._animation = null;
|
|
2043
|
+
animationCompleted = true;
|
|
2044
|
+
dispatchGaugeEvent(this.host, "animationend", {
|
|
2045
|
+
gauge: this,
|
|
2046
|
+
value: completed.to
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
const jitter = this.options.effects.jitter;
|
|
2051
|
+
const jitterActive = jitter.enabled && (!jitter.whenIdleOnly || !this._animation);
|
|
2052
|
+
if (jitterActive) {
|
|
2053
|
+
const interval = 1e3 / jitter.frequency;
|
|
2054
|
+
if (this._lastJitterTargetAt === 0 || now - this._lastJitterTargetAt >= interval) {
|
|
2055
|
+
this._jitterTarget = (Math.random() * 2 - 1) * this._jitterAmplitude();
|
|
2056
|
+
this._lastJitterTargetAt = now;
|
|
2057
|
+
}
|
|
2058
|
+
const smoothingFactor = 1 - Math.pow(jitter.smoothing, delta / 16.667);
|
|
2059
|
+
this._jitterOffset += (this._jitterTarget - this._jitterOffset) * smoothingFactor;
|
|
2060
|
+
} else {
|
|
2061
|
+
this._jitterTarget = 0;
|
|
2062
|
+
const smoothingFactor = 1 - Math.pow(jitter.smoothing, delta / 16.667);
|
|
2063
|
+
this._jitterOffset += (0 - this._jitterOffset) * smoothingFactor;
|
|
2064
|
+
if (Math.abs(this._jitterOffset) < 1e-6) this._jitterOffset = 0;
|
|
2065
|
+
}
|
|
2066
|
+
const nextDisplay = this._displayClamp(this._baseValue);
|
|
2067
|
+
const nextPointer = this._displayClamp(this._baseValue + this._jitterOffset);
|
|
2068
|
+
this._updateDynamic(nextDisplay, false, nextPointer);
|
|
2069
|
+
if (animationCompleted) this._emitSettled();
|
|
2070
|
+
if (this._needsAnotherFrame()) this._ensureLoop();
|
|
2071
|
+
}
|
|
2072
|
+
_needsAnotherFrame() {
|
|
2073
|
+
if (this._animation) return true;
|
|
2074
|
+
const jitter = this.options.effects.jitter;
|
|
2075
|
+
if (jitter.enabled) return true;
|
|
2076
|
+
return Math.abs(this._jitterOffset) > 1e-6;
|
|
2077
|
+
}
|
|
2078
|
+
_jitterAmplitude() {
|
|
2079
|
+
const jitter = this.options.effects.jitter;
|
|
2080
|
+
if (jitter.amplitudeUnit === "percent") {
|
|
2081
|
+
return (this.options.max - this.options.min) * jitter.amplitude / 100;
|
|
2082
|
+
}
|
|
2083
|
+
return jitter.amplitude;
|
|
2084
|
+
}
|
|
2085
|
+
_displayClamp(value) {
|
|
2086
|
+
return this.options.overflow === "allow" ? value : clamp(value, this.options.min, this.options.max);
|
|
2087
|
+
}
|
|
2088
|
+
_updateDynamic(value, force, pointerValue = value) {
|
|
2089
|
+
const rounded = roundValue(value, this.options.precision);
|
|
2090
|
+
const displayChanged = !Object.is(rounded, this._displayValue);
|
|
2091
|
+
const pointerChanged = !Object.is(pointerValue, this._pointerValue);
|
|
2092
|
+
if (!force && !displayChanged && !pointerChanged) return;
|
|
2093
|
+
this._displayValue = rounded;
|
|
2094
|
+
this._pointerValue = pointerValue;
|
|
2095
|
+
this.renderer.update(rounded, {
|
|
2096
|
+
gauge: this,
|
|
2097
|
+
targetValue: this._targetValue,
|
|
2098
|
+
baseValue: this._baseValue,
|
|
2099
|
+
pointerValue,
|
|
2100
|
+
animated: Boolean(this._animation),
|
|
2101
|
+
jitterOffset: this._jitterOffset
|
|
2102
|
+
});
|
|
2103
|
+
if (force || displayChanged) {
|
|
2104
|
+
this._updateAccessibility(rounded);
|
|
2105
|
+
dispatchGaugeEvent(this.host, "displaychange", {
|
|
2106
|
+
gauge: this,
|
|
2107
|
+
value: this._targetValue,
|
|
2108
|
+
displayedValue: rounded
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
_updateAccessibility(value) {
|
|
2113
|
+
const root = this.renderer.root;
|
|
2114
|
+
if (!root) return;
|
|
2115
|
+
root.setAttribute("role", "meter");
|
|
2116
|
+
root.setAttribute("aria-label", this.options.accessibility.label);
|
|
2117
|
+
root.setAttribute("aria-valuemin", String(this.options.min));
|
|
2118
|
+
root.setAttribute("aria-valuemax", String(this.options.max));
|
|
2119
|
+
root.setAttribute("aria-valuenow", String(value));
|
|
2120
|
+
const valueText = this.options.accessibility.valueText;
|
|
2121
|
+
if (typeof valueText === "function") {
|
|
2122
|
+
root.setAttribute("aria-valuetext", String(valueText(value, this.options)));
|
|
2123
|
+
} else if (typeof valueText === "string" && valueText) {
|
|
2124
|
+
root.setAttribute("aria-valuetext", valueText);
|
|
2125
|
+
} else {
|
|
2126
|
+
root.removeAttribute("aria-valuetext");
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
_emitChange() {
|
|
2130
|
+
dispatchGaugeEvent(this.host, "change", {
|
|
2131
|
+
gauge: this,
|
|
2132
|
+
value: this._targetValue,
|
|
2133
|
+
displayedValue: this._displayValue
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
2136
|
+
_emitSettled() {
|
|
2137
|
+
dispatchGaugeEvent(this.host, "settled", {
|
|
2138
|
+
gauge: this,
|
|
2139
|
+
value: this._targetValue,
|
|
2140
|
+
displayedValue: this._displayValue
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
_syncLifecycleObservers() {
|
|
2144
|
+
this._removeLifecycleObservers();
|
|
2145
|
+
this._onscreen = true;
|
|
2146
|
+
this._lifecycleSuspendedAt = 0;
|
|
2147
|
+
this._installLifecycleObservers();
|
|
2148
|
+
}
|
|
2149
|
+
_removeLifecycleObservers() {
|
|
2150
|
+
if (this._visibilityHandler && typeof document !== "undefined") {
|
|
2151
|
+
document.removeEventListener("visibilitychange", this._visibilityHandler);
|
|
2152
|
+
}
|
|
2153
|
+
this._visibilityHandler = null;
|
|
2154
|
+
if (this._intersectionObserver) this._intersectionObserver.disconnect();
|
|
2155
|
+
this._intersectionObserver = null;
|
|
2156
|
+
}
|
|
2157
|
+
_installLifecycleObservers() {
|
|
2158
|
+
if (typeof document !== "undefined" && this.options.performance.pauseWhenHidden) {
|
|
2159
|
+
this._visibilityHandler = () => {
|
|
2160
|
+
if (document.hidden) {
|
|
2161
|
+
this._suspendForLifecycle();
|
|
2162
|
+
return;
|
|
2163
|
+
}
|
|
2164
|
+
this._resumeFromLifecycle();
|
|
2165
|
+
};
|
|
2166
|
+
document.addEventListener("visibilitychange", this._visibilityHandler, { passive: true });
|
|
2167
|
+
if (document.hidden) this._suspendForLifecycle();
|
|
2168
|
+
}
|
|
2169
|
+
if (typeof IntersectionObserver === "function" && this.options.performance.pauseWhenOffscreen) {
|
|
2170
|
+
this._intersectionObserver = new IntersectionObserver((entries) => {
|
|
2171
|
+
const entry = entries[0];
|
|
2172
|
+
this._onscreen = Boolean(entry && entry.isIntersecting);
|
|
2173
|
+
if (!this._onscreen) {
|
|
2174
|
+
this._suspendForLifecycle();
|
|
2175
|
+
} else {
|
|
2176
|
+
this._resumeFromLifecycle();
|
|
2177
|
+
}
|
|
2178
|
+
}, { rootMargin: "100px" });
|
|
2179
|
+
this._intersectionObserver.observe(this.host);
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
_shouldRun() {
|
|
2183
|
+
if (this._isLifecycleBlocked()) return false;
|
|
2184
|
+
return this._needsAnotherFrame();
|
|
2185
|
+
}
|
|
2186
|
+
_isLifecycleBlocked() {
|
|
2187
|
+
if (typeof document !== "undefined" && this.options.performance.pauseWhenHidden && document.hidden) return true;
|
|
2188
|
+
return this.options.performance.pauseWhenOffscreen && !this._onscreen;
|
|
2189
|
+
}
|
|
2190
|
+
_suspendForLifecycle() {
|
|
2191
|
+
if (this._lifecycleSuspendedAt === 0) this._lifecycleSuspendedAt = performanceNow();
|
|
2192
|
+
cancelFrame(this._frameHandle);
|
|
2193
|
+
this._frameHandle = null;
|
|
2194
|
+
}
|
|
2195
|
+
_resumeFromLifecycle() {
|
|
2196
|
+
if (typeof document !== "undefined" && this.options.performance.pauseWhenHidden && document.hidden) return;
|
|
2197
|
+
if (this.options.performance.pauseWhenOffscreen && !this._onscreen) return;
|
|
2198
|
+
if (this._paused) return;
|
|
2199
|
+
if (this._lifecycleSuspendedAt > 0 && this._animation) {
|
|
2200
|
+
this._animation.startedAt += Math.max(0, performanceNow() - this._lifecycleSuspendedAt);
|
|
2201
|
+
}
|
|
2202
|
+
this._lifecycleSuspendedAt = 0;
|
|
2203
|
+
this._lastFrameAt = 0;
|
|
2204
|
+
this._ensureLoop();
|
|
2205
|
+
}
|
|
2206
|
+
_prefersReducedMotion() {
|
|
2207
|
+
if (!this.options.animation.respectReducedMotion || typeof matchMedia !== "function") return false;
|
|
2208
|
+
return matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
2209
|
+
}
|
|
2210
|
+
_assertAlive() {
|
|
2211
|
+
if (this._destroyed) throw new Error("This Gaugeit instance has been destroyed.");
|
|
2212
|
+
}
|
|
2213
|
+
};
|
|
2214
|
+
function performanceNow() {
|
|
2215
|
+
if (typeof performance !== "undefined" && typeof performance.now === "function") return performance.now();
|
|
2216
|
+
return Date.now();
|
|
2217
|
+
}
|
|
2218
|
+
function roundValue(value, precision) {
|
|
2219
|
+
const factor = Math.pow(10, precision);
|
|
2220
|
+
return Math.round(value * factor) / factor;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
// src/core/registry.js
|
|
2224
|
+
var GaugeTypeRegistry = class {
|
|
2225
|
+
constructor() {
|
|
2226
|
+
this._types = /* @__PURE__ */ new Map();
|
|
2227
|
+
}
|
|
2228
|
+
/** Registers or replaces a gauge type. */
|
|
2229
|
+
register(name, definition) {
|
|
2230
|
+
const normalizedName = String(name || "").trim();
|
|
2231
|
+
if (!normalizedName) throw new TypeError("Gauge type name must be a non-empty string.");
|
|
2232
|
+
if (!isPlainObject(definition)) throw new TypeError(`Gauge type "${normalizedName}" must be an object.`);
|
|
2233
|
+
if (!Array.isArray(definition.layers) || definition.layers.length === 0) {
|
|
2234
|
+
throw new TypeError(`Gauge type "${normalizedName}" must define at least one layer.`);
|
|
2235
|
+
}
|
|
2236
|
+
const normalizedLayers = definition.layers.map((layer, index) => {
|
|
2237
|
+
if (!layer || typeof layer.render !== "function") {
|
|
2238
|
+
throw new TypeError(`Layer ${index} in gauge type "${normalizedName}" must expose render(context).`);
|
|
2239
|
+
}
|
|
2240
|
+
return layer;
|
|
2241
|
+
});
|
|
2242
|
+
this._types.set(normalizedName, {
|
|
2243
|
+
name: normalizedName,
|
|
2244
|
+
defaults: cloneValue(definition.defaults || {}),
|
|
2245
|
+
layers: normalizedLayers,
|
|
2246
|
+
description: String(definition.description || ""),
|
|
2247
|
+
configure: typeof definition.configure === "function" ? definition.configure : null
|
|
2248
|
+
});
|
|
2249
|
+
return this;
|
|
2250
|
+
}
|
|
2251
|
+
/** Returns a registered gauge type definition. */
|
|
2252
|
+
get(name) {
|
|
2253
|
+
const definition = this._types.get(String(name));
|
|
2254
|
+
if (!definition) throw new Error(`Unknown Gaugeit gauge type: ${name}`);
|
|
2255
|
+
return definition;
|
|
2256
|
+
}
|
|
2257
|
+
/** Returns whether a type exists. */
|
|
2258
|
+
has(name) {
|
|
2259
|
+
return this._types.has(String(name));
|
|
2260
|
+
}
|
|
2261
|
+
/** Returns registered type names. */
|
|
2262
|
+
names() {
|
|
2263
|
+
return [...this._types.keys()];
|
|
2264
|
+
}
|
|
2265
|
+
/** Returns a serializable description of the registry. */
|
|
2266
|
+
describe() {
|
|
2267
|
+
return [...this._types.values()].map((definition) => ({
|
|
2268
|
+
name: definition.name,
|
|
2269
|
+
description: definition.description
|
|
2270
|
+
}));
|
|
2271
|
+
}
|
|
2272
|
+
};
|
|
2273
|
+
var gaugeTypeRegistry = new GaugeTypeRegistry();
|
|
2274
|
+
|
|
2275
|
+
// src/layers/face.js
|
|
2276
|
+
var faceLayer = {
|
|
2277
|
+
id: "face",
|
|
2278
|
+
render(context) {
|
|
2279
|
+
const { options, renderer } = context;
|
|
2280
|
+
const face = options.face;
|
|
2281
|
+
if (!face.visible) return null;
|
|
2282
|
+
const group = renderer.group("face", { "aria-hidden": "true" });
|
|
2283
|
+
const box = resolveFaceBox(options, renderer.getContentBounds());
|
|
2284
|
+
const shadowId = `${renderer.id}-face-shadow`;
|
|
2285
|
+
const glassId = `${renderer.id}-glass`;
|
|
2286
|
+
const clipId = `${renderer.id}-face-content-clip`;
|
|
2287
|
+
const inset = Math.max(3, face.strokeWidth + 2);
|
|
2288
|
+
const innerBox = insetFaceBox(box, inset);
|
|
2289
|
+
const defs = svgElement("defs");
|
|
2290
|
+
if (face.shadow) {
|
|
2291
|
+
const filter = svgElement("filter", {
|
|
2292
|
+
id: shadowId,
|
|
2293
|
+
x: "-20%",
|
|
2294
|
+
y: "-20%",
|
|
2295
|
+
width: "140%",
|
|
2296
|
+
height: "140%"
|
|
2297
|
+
});
|
|
2298
|
+
filter.append(svgElement("feDropShadow", {
|
|
2299
|
+
dx: 0,
|
|
2300
|
+
dy: 3,
|
|
2301
|
+
stdDeviation: 4,
|
|
2302
|
+
"flood-color": "#000000",
|
|
2303
|
+
"flood-opacity": 0.18
|
|
2304
|
+
}));
|
|
2305
|
+
defs.append(filter);
|
|
2306
|
+
}
|
|
2307
|
+
if (face.glass) {
|
|
2308
|
+
const gradient = svgElement("linearGradient", {
|
|
2309
|
+
id: glassId,
|
|
2310
|
+
x1: "0%",
|
|
2311
|
+
y1: "0%",
|
|
2312
|
+
x2: "0%",
|
|
2313
|
+
y2: "100%"
|
|
2314
|
+
});
|
|
2315
|
+
gradient.append(
|
|
2316
|
+
svgElement("stop", { offset: "0%", "stop-color": "#ffffff", "stop-opacity": 0.38 }),
|
|
2317
|
+
svgElement("stop", { offset: "48%", "stop-color": "#ffffff", "stop-opacity": 0.06 }),
|
|
2318
|
+
svgElement("stop", { offset: "100%", "stop-color": "#ffffff", "stop-opacity": 0 })
|
|
2319
|
+
);
|
|
2320
|
+
defs.append(gradient);
|
|
2321
|
+
}
|
|
2322
|
+
if (face.clipContent) {
|
|
2323
|
+
const clipPath = svgElement("clipPath", {
|
|
2324
|
+
id: clipId,
|
|
2325
|
+
clipPathUnits: "userSpaceOnUse"
|
|
2326
|
+
});
|
|
2327
|
+
clipPath.append(createFaceShape(face, innerBox, {
|
|
2328
|
+
fill: "#ffffff",
|
|
2329
|
+
stroke: "none"
|
|
2330
|
+
}, Math.max(0, face.cornerRadius - inset)));
|
|
2331
|
+
defs.append(clipPath);
|
|
2332
|
+
renderer.setContentClipPath(clipId);
|
|
2333
|
+
}
|
|
2334
|
+
if (defs.children.length > 0) group.append(defs);
|
|
2335
|
+
const base = createFaceShape(face, box, {
|
|
2336
|
+
fill: face.color,
|
|
2337
|
+
stroke: face.strokeColor,
|
|
2338
|
+
"stroke-width": face.strokeWidth,
|
|
2339
|
+
filter: face.shadow ? `url(#${shadowId})` : void 0
|
|
2340
|
+
});
|
|
2341
|
+
base.classList.add("gaugeit__face");
|
|
2342
|
+
group.append(base);
|
|
2343
|
+
group.append(createFaceShape(face, innerBox, {
|
|
2344
|
+
class: "gaugeit__face-inner",
|
|
2345
|
+
fill: "none",
|
|
2346
|
+
stroke: face.innerStrokeColor,
|
|
2347
|
+
"stroke-width": face.innerStrokeWidth
|
|
2348
|
+
}, Math.max(0, face.cornerRadius - inset)));
|
|
2349
|
+
if (face.glass) {
|
|
2350
|
+
if (face.shape === "circle") {
|
|
2351
|
+
const radius = Math.min(box.width, box.height) / 2;
|
|
2352
|
+
group.append(svgElement("ellipse", {
|
|
2353
|
+
class: "gaugeit__glass",
|
|
2354
|
+
cx: box.x + box.width / 2,
|
|
2355
|
+
cy: box.y + box.height / 2 - radius * 0.27,
|
|
2356
|
+
rx: radius * 0.68,
|
|
2357
|
+
ry: radius * 0.32,
|
|
2358
|
+
fill: `url(#${glassId})`,
|
|
2359
|
+
"pointer-events": "none"
|
|
2360
|
+
}));
|
|
2361
|
+
} else {
|
|
2362
|
+
group.append(svgElement("path", {
|
|
2363
|
+
class: "gaugeit__glass",
|
|
2364
|
+
d: `M ${box.x + 14} ${box.y + 14} H ${box.x + box.width - 14} V ${box.y + box.height * 0.52} Q ${box.x + box.width / 2} ${box.y + box.height * 0.38} ${box.x + 14} ${box.y + box.height * 0.52} Z`,
|
|
2365
|
+
fill: `url(#${glassId})`,
|
|
2366
|
+
"pointer-events": "none"
|
|
2367
|
+
}));
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
return null;
|
|
2371
|
+
}
|
|
2372
|
+
};
|
|
2373
|
+
function insetFaceBox(box, inset) {
|
|
2374
|
+
return {
|
|
2375
|
+
x: box.x + inset,
|
|
2376
|
+
y: box.y + inset,
|
|
2377
|
+
width: Math.max(1, box.width - inset * 2),
|
|
2378
|
+
height: Math.max(1, box.height - inset * 2)
|
|
2379
|
+
};
|
|
2380
|
+
}
|
|
2381
|
+
function createFaceShape(face, box, attributes = {}, cornerRadius = face.cornerRadius) {
|
|
2382
|
+
if (face.shape === "circle") {
|
|
2383
|
+
return svgElement("circle", {
|
|
2384
|
+
cx: box.x + box.width / 2,
|
|
2385
|
+
cy: box.y + box.height / 2,
|
|
2386
|
+
r: Math.max(1, Math.min(box.width, box.height) / 2),
|
|
2387
|
+
...attributes
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
const radius = Math.max(0, Math.min(cornerRadius, box.width / 2, box.height / 2));
|
|
2391
|
+
return svgElement("rect", {
|
|
2392
|
+
x: box.x,
|
|
2393
|
+
y: box.y,
|
|
2394
|
+
width: box.width,
|
|
2395
|
+
height: box.height,
|
|
2396
|
+
rx: radius,
|
|
2397
|
+
ry: radius,
|
|
2398
|
+
...attributes
|
|
2399
|
+
});
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
// src/layers/zones.js
|
|
2403
|
+
var zonesLayer = {
|
|
2404
|
+
id: "zones",
|
|
2405
|
+
render(context) {
|
|
2406
|
+
const { options, renderer } = context;
|
|
2407
|
+
const group = renderer.group("zones", { "aria-hidden": "true" });
|
|
2408
|
+
const segments = resolveZoneSegments(options.min, options.max, options.zones);
|
|
2409
|
+
if (options.track.showUnderZones && options.track.visible) {
|
|
2410
|
+
appendArc(group, options.min, options.max, options.track, "gaugeit__track");
|
|
2411
|
+
}
|
|
2412
|
+
for (const segment of segments) {
|
|
2413
|
+
const style = segment.zone || options.track;
|
|
2414
|
+
const shouldDraw = segment.zone ? !isTransparentColor(segment.zone.color) : options.track.visible && !options.track.showUnderZones;
|
|
2415
|
+
if (!shouldDraw) continue;
|
|
2416
|
+
appendArc(
|
|
2417
|
+
group,
|
|
2418
|
+
segment.min,
|
|
2419
|
+
segment.max,
|
|
2420
|
+
style,
|
|
2421
|
+
segment.zone ? "gaugeit__zone" : "gaugeit__track"
|
|
2422
|
+
);
|
|
2423
|
+
}
|
|
2424
|
+
return null;
|
|
2425
|
+
function appendArc(parent, min, max, style, className) {
|
|
2426
|
+
const nodes = createArcNodes(min, max, style, className, options);
|
|
2427
|
+
for (const node of nodes) {
|
|
2428
|
+
if (style.className) node.classList.add(String(style.className));
|
|
2429
|
+
parent.append(node);
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
};
|
|
2434
|
+
function createArcNodes(min, max, style, className, options) {
|
|
2435
|
+
var _a, _b, _c, _d, _e;
|
|
2436
|
+
const geometry2 = options.geometry;
|
|
2437
|
+
const radius = (_b = (_a = style.radius) != null ? _a : options.track.radius) != null ? _b : geometry2.radius;
|
|
2438
|
+
const fallbackWidth = (_c = style.width) != null ? _c : options.track.width;
|
|
2439
|
+
const color = (_d = style.color) != null ? _d : options.track.color;
|
|
2440
|
+
const opacity = (_e = style.opacity) != null ? _e : 1;
|
|
2441
|
+
const lineCap = style.lineCap || options.track.lineCap || "butt";
|
|
2442
|
+
const startAngle = valueToAngle(min, options);
|
|
2443
|
+
const endAngle = valueToAngle(max, options);
|
|
2444
|
+
if (!style.taper) {
|
|
2445
|
+
return [svgElement("path", {
|
|
2446
|
+
class: className,
|
|
2447
|
+
d: arcPath(geometry2.centerX, geometry2.centerY, radius, startAngle, endAngle),
|
|
2448
|
+
fill: "none",
|
|
2449
|
+
stroke: color,
|
|
2450
|
+
"stroke-width": fallbackWidth,
|
|
2451
|
+
"stroke-linecap": lineCap,
|
|
2452
|
+
opacity
|
|
2453
|
+
})];
|
|
2454
|
+
}
|
|
2455
|
+
const startWidth = widthAtValue(style, min, fallbackWidth);
|
|
2456
|
+
const endWidth = widthAtValue(style, max, fallbackWidth);
|
|
2457
|
+
const nodes = [svgElement("path", {
|
|
2458
|
+
class: `${className} ${className}--tapered`,
|
|
2459
|
+
d: taperedArcPath(
|
|
2460
|
+
geometry2.centerX,
|
|
2461
|
+
geometry2.centerY,
|
|
2462
|
+
radius,
|
|
2463
|
+
startAngle,
|
|
2464
|
+
endAngle,
|
|
2465
|
+
startWidth,
|
|
2466
|
+
endWidth,
|
|
2467
|
+
style.taper.segments
|
|
2468
|
+
),
|
|
2469
|
+
fill: color,
|
|
2470
|
+
stroke: "none",
|
|
2471
|
+
opacity
|
|
2472
|
+
})];
|
|
2473
|
+
if (lineCap === "round") {
|
|
2474
|
+
const start = polarPoint(geometry2.centerX, geometry2.centerY, radius, startAngle);
|
|
2475
|
+
const end = polarPoint(geometry2.centerX, geometry2.centerY, radius, endAngle);
|
|
2476
|
+
const atZoneStart = Math.abs(min - style.min) < 1e-9;
|
|
2477
|
+
const atZoneEnd = Math.abs(max - style.max) < 1e-9;
|
|
2478
|
+
if (atZoneStart && startWidth > 0) nodes.push(capNode(className, start, startWidth, color, opacity, "start"));
|
|
2479
|
+
if (atZoneEnd && endWidth > 0) nodes.push(capNode(className, end, endWidth, color, opacity, "end"));
|
|
2480
|
+
}
|
|
2481
|
+
return nodes;
|
|
2482
|
+
}
|
|
2483
|
+
function widthAtValue(zone, value, fallbackWidth) {
|
|
2484
|
+
const taper = zone.taper || {};
|
|
2485
|
+
const startWidth = Math.max(0, finiteNumber(taper.startWidth, fallbackWidth));
|
|
2486
|
+
const endWidth = Math.max(0, finiteNumber(taper.endWidth, fallbackWidth));
|
|
2487
|
+
const range = Math.max(1e-12, zone.max - zone.min);
|
|
2488
|
+
const ratio = Math.min(1, Math.max(0, (value - zone.min) / range));
|
|
2489
|
+
return startWidth + (endWidth - startWidth) * ratio;
|
|
2490
|
+
}
|
|
2491
|
+
function capNode(className, point, width, color, opacity, edge) {
|
|
2492
|
+
return svgElement("circle", {
|
|
2493
|
+
class: `${className} ${className}--taper-cap`,
|
|
2494
|
+
"data-gaugeit-zone-cap": edge,
|
|
2495
|
+
cx: point.x,
|
|
2496
|
+
cy: point.y,
|
|
2497
|
+
r: width / 2,
|
|
2498
|
+
fill: color,
|
|
2499
|
+
opacity
|
|
2500
|
+
});
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
// src/layers/ticks.js
|
|
2504
|
+
var ticksLayer = {
|
|
2505
|
+
id: "ticks",
|
|
2506
|
+
render(context) {
|
|
2507
|
+
const { options, renderer } = context;
|
|
2508
|
+
const scale = options.scale;
|
|
2509
|
+
if (!scale.major.visible && !scale.minor.visible) return null;
|
|
2510
|
+
const group = renderer.group("ticks", { "aria-hidden": "true" });
|
|
2511
|
+
const interval = resolveMajorInterval(options);
|
|
2512
|
+
const completeMajorValues = buildScaleValues(options.min, options.max, interval);
|
|
2513
|
+
const majorValues = resolveScaleEndpointValues(completeMajorValues, options);
|
|
2514
|
+
const minorValues = buildMinorValues(completeMajorValues, scale.minor.subdivisions);
|
|
2515
|
+
if (scale.minor.visible && scale.minor.length > 0 && scale.minor.width > 0 && minorValues.length) {
|
|
2516
|
+
group.append(svgElement("path", {
|
|
2517
|
+
class: "gaugeit__ticks gaugeit__ticks--minor",
|
|
2518
|
+
d: tickPath(minorValues, scale.minor.length),
|
|
2519
|
+
fill: "none",
|
|
2520
|
+
stroke: scale.minor.color,
|
|
2521
|
+
"stroke-width": scale.minor.width,
|
|
2522
|
+
"stroke-linecap": "butt",
|
|
2523
|
+
opacity: scale.minor.opacity
|
|
2524
|
+
}));
|
|
2525
|
+
}
|
|
2526
|
+
if (scale.major.visible && scale.major.length > 0 && scale.major.width > 0) {
|
|
2527
|
+
group.append(svgElement("path", {
|
|
2528
|
+
class: "gaugeit__ticks gaugeit__ticks--major",
|
|
2529
|
+
d: tickPath(majorValues, scale.major.length),
|
|
2530
|
+
fill: "none",
|
|
2531
|
+
stroke: scale.major.color,
|
|
2532
|
+
"stroke-width": scale.major.width,
|
|
2533
|
+
"stroke-linecap": "butt",
|
|
2534
|
+
opacity: scale.major.opacity
|
|
2535
|
+
}));
|
|
2536
|
+
}
|
|
2537
|
+
renderer.setReference("majorValues", majorValues);
|
|
2538
|
+
return null;
|
|
2539
|
+
function tickPath(values, length) {
|
|
2540
|
+
const commands = [];
|
|
2541
|
+
for (const value of values) {
|
|
2542
|
+
const angle = valueToAngle(value, options);
|
|
2543
|
+
const radii = tickRadii(scale.radius, length, scale.position);
|
|
2544
|
+
const start = polarPoint(options.geometry.centerX, options.geometry.centerY, radii.start, angle);
|
|
2545
|
+
const end = polarPoint(options.geometry.centerX, options.geometry.centerY, radii.end, angle);
|
|
2546
|
+
commands.push(`M ${roundSvg(start.x)} ${roundSvg(start.y)} L ${roundSvg(end.x)} ${roundSvg(end.y)}`);
|
|
2547
|
+
}
|
|
2548
|
+
return commands.join(" ");
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
};
|
|
2552
|
+
function tickRadii(radius, length, position) {
|
|
2553
|
+
if (position === "outside") return { start: radius, end: radius + length };
|
|
2554
|
+
if (position === "cross") return { start: radius - length / 2, end: radius + length / 2 };
|
|
2555
|
+
return { start: radius, end: radius - length };
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
// src/layers/labels.js
|
|
2559
|
+
var labelsLayer = {
|
|
2560
|
+
id: "labels",
|
|
2561
|
+
render(context) {
|
|
2562
|
+
const { options, renderer } = context;
|
|
2563
|
+
const labels = options.labels;
|
|
2564
|
+
if (!labels.visible) return null;
|
|
2565
|
+
const group = renderer.group("labels", { "aria-hidden": "true" });
|
|
2566
|
+
const interval = Number(labels.interval) > 0 ? Number(labels.interval) : resolveMajorInterval(options);
|
|
2567
|
+
const values = resolveScaleEndpointValues(
|
|
2568
|
+
buildScaleValues(options.min, options.max, interval),
|
|
2569
|
+
options
|
|
2570
|
+
);
|
|
2571
|
+
for (const value of values) {
|
|
2572
|
+
const angle = valueToAngle(value, options);
|
|
2573
|
+
const point = polarPoint(options.geometry.centerX, options.geometry.centerY, labels.radius, angle);
|
|
2574
|
+
const text = svgElement("text", {
|
|
2575
|
+
class: "gaugeit__label",
|
|
2576
|
+
x: point.x,
|
|
2577
|
+
y: point.y,
|
|
2578
|
+
fill: labels.color,
|
|
2579
|
+
"font-family": labels.fontFamily,
|
|
2580
|
+
"font-size": labels.fontSize,
|
|
2581
|
+
"font-weight": labels.fontWeight,
|
|
2582
|
+
"text-anchor": "middle",
|
|
2583
|
+
"dominant-baseline": "middle",
|
|
2584
|
+
transform: labelTransform(labels.rotation, angle, point)
|
|
2585
|
+
}, formatLabel(value, labels, options));
|
|
2586
|
+
group.append(text);
|
|
2587
|
+
}
|
|
2588
|
+
return null;
|
|
2589
|
+
}
|
|
2590
|
+
};
|
|
2591
|
+
function labelTransform(rotation, angle, point) {
|
|
2592
|
+
if (rotation === "radial") return `rotate(${angle} ${point.x} ${point.y})`;
|
|
2593
|
+
if (rotation === "tangent") return `rotate(${angle + 90} ${point.x} ${point.y})`;
|
|
2594
|
+
return void 0;
|
|
2595
|
+
}
|
|
2596
|
+
function formatLabel(value, labels, options) {
|
|
2597
|
+
if (typeof labels.formatter === "function") {
|
|
2598
|
+
return labels.formatter(value, { min: options.min, max: options.max, options });
|
|
2599
|
+
}
|
|
2600
|
+
return Number(value).toFixed(labels.fractionDigits);
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
// src/layers/pointer.js
|
|
2604
|
+
var pointerLayer = {
|
|
2605
|
+
id: "pointer",
|
|
2606
|
+
render(context) {
|
|
2607
|
+
const { options, renderer } = context;
|
|
2608
|
+
const pointer = options.pointer;
|
|
2609
|
+
if (!pointer.visible) return null;
|
|
2610
|
+
const geometry2 = options.geometry;
|
|
2611
|
+
const group = renderer.group("pointer", { "aria-hidden": "true" });
|
|
2612
|
+
const rotating = svgElement("g", { class: "gaugeit__pointer-rotating" });
|
|
2613
|
+
const shape = createPointerShape(pointer, geometry2);
|
|
2614
|
+
rotating.append(shape);
|
|
2615
|
+
let cap = null;
|
|
2616
|
+
if (pointer.cap.visible) {
|
|
2617
|
+
cap = svgElement("circle", {
|
|
2618
|
+
class: "gaugeit__pointer-cap",
|
|
2619
|
+
cx: geometry2.centerX,
|
|
2620
|
+
cy: geometry2.centerY,
|
|
2621
|
+
r: pointer.cap.radius,
|
|
2622
|
+
fill: pointer.cap.color,
|
|
2623
|
+
stroke: pointer.cap.strokeColor,
|
|
2624
|
+
"stroke-width": pointer.cap.strokeWidth
|
|
2625
|
+
});
|
|
2626
|
+
}
|
|
2627
|
+
appendVisualStack(group, rotating, cap, pointer.cap.stackOrder);
|
|
2628
|
+
return {
|
|
2629
|
+
update(value, state = {}) {
|
|
2630
|
+
const pointerValue = Number.isFinite(state.pointerValue) ? state.pointerValue : value;
|
|
2631
|
+
const angle = valueToAngle(pointerValue, options, options.overflow !== "allow");
|
|
2632
|
+
rotating.setAttribute("transform", `rotate(${angle} ${geometry2.centerX} ${geometry2.centerY})`);
|
|
2633
|
+
if (pointer.colorByZone) {
|
|
2634
|
+
const zone = findZoneForValue(pointerValue, options.zones);
|
|
2635
|
+
const color = zone && !isTransparentColor(zone.color) ? zone.color : pointer.color;
|
|
2636
|
+
if (shape.tagName.toLowerCase() === "line") shape.setAttribute("stroke", color);
|
|
2637
|
+
else shape.setAttribute("fill", color);
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2642
|
+
};
|
|
2643
|
+
function appendVisualStack(group, pointer, cap, capStackOrder) {
|
|
2644
|
+
if (cap && capStackOrder === "below") group.append(cap, pointer);
|
|
2645
|
+
else if (cap) group.append(pointer, cap);
|
|
2646
|
+
else group.append(pointer);
|
|
2647
|
+
}
|
|
2648
|
+
function createPointerShape(pointer, geometry2) {
|
|
2649
|
+
const cx = geometry2.centerX;
|
|
2650
|
+
const cy = geometry2.centerY;
|
|
2651
|
+
const front = pointer.length;
|
|
2652
|
+
const tail = pointer.tailLength;
|
|
2653
|
+
const half = pointer.width / 2;
|
|
2654
|
+
const shaft = resolveShaftLength(pointer);
|
|
2655
|
+
let shape;
|
|
2656
|
+
if (pointer.type === "line") {
|
|
2657
|
+
shape = svgElement("line", {
|
|
2658
|
+
class: "gaugeit__pointer gaugeit__pointer--line",
|
|
2659
|
+
x1: cx - tail,
|
|
2660
|
+
y1: cy,
|
|
2661
|
+
x2: cx + front,
|
|
2662
|
+
y2: cy,
|
|
2663
|
+
stroke: pointer.color,
|
|
2664
|
+
"stroke-width": pointer.width,
|
|
2665
|
+
"stroke-linecap": "round"
|
|
2666
|
+
});
|
|
2667
|
+
} else {
|
|
2668
|
+
let points;
|
|
2669
|
+
if (pointer.type === "arrow") {
|
|
2670
|
+
points = [
|
|
2671
|
+
[cx - tail, cy - half * 0.55],
|
|
2672
|
+
[cx + shaft, cy - half * 0.55],
|
|
2673
|
+
[cx + shaft, cy - half * 1.5],
|
|
2674
|
+
[cx + front, cy],
|
|
2675
|
+
[cx + shaft, cy + half * 1.5],
|
|
2676
|
+
[cx + shaft, cy + half * 0.55],
|
|
2677
|
+
[cx - tail, cy + half * 0.55]
|
|
2678
|
+
];
|
|
2679
|
+
} else if (pointer.type === "spear") {
|
|
2680
|
+
const shoulder = shaft + (front - shaft) * 0.56;
|
|
2681
|
+
points = [
|
|
2682
|
+
[cx - tail, cy - half * 0.45],
|
|
2683
|
+
[cx + shaft, cy - half * 0.38],
|
|
2684
|
+
[cx + shoulder, cy - half * 0.85],
|
|
2685
|
+
[cx + front, cy],
|
|
2686
|
+
[cx + shoulder, cy + half * 0.85],
|
|
2687
|
+
[cx + shaft, cy + half * 0.38],
|
|
2688
|
+
[cx - tail, cy + half * 0.45]
|
|
2689
|
+
];
|
|
2690
|
+
} else {
|
|
2691
|
+
points = [
|
|
2692
|
+
[cx - tail, cy - half * 0.65],
|
|
2693
|
+
[cx + shaft, cy - half * 0.22],
|
|
2694
|
+
[cx + front, cy],
|
|
2695
|
+
[cx + shaft, cy + half * 0.22],
|
|
2696
|
+
[cx - tail, cy + half * 0.65]
|
|
2697
|
+
];
|
|
2698
|
+
}
|
|
2699
|
+
const path = points.map((point, index) => `${index === 0 ? "M" : "L"} ${point[0]} ${point[1]}`).join(" ");
|
|
2700
|
+
shape = svgElement("path", {
|
|
2701
|
+
class: `gaugeit__pointer gaugeit__pointer--${pointer.type}`,
|
|
2702
|
+
d: `${path} Z`,
|
|
2703
|
+
fill: pointer.color
|
|
2704
|
+
});
|
|
2705
|
+
}
|
|
2706
|
+
setSvgAttributes(shape, {
|
|
2707
|
+
opacity: pointer.opacity,
|
|
2708
|
+
stroke: pointer.type === "line" ? pointer.color : pointer.strokeColor,
|
|
2709
|
+
"stroke-width": pointer.type === "line" ? pointer.width : pointer.strokeWidth
|
|
2710
|
+
});
|
|
2711
|
+
return shape;
|
|
2712
|
+
}
|
|
2713
|
+
function resolveShaftLength(pointer) {
|
|
2714
|
+
if (Number.isFinite(pointer.shaftLength)) return Math.min(pointer.length, Math.max(0, pointer.shaftLength));
|
|
2715
|
+
if (pointer.type === "arrow") return pointer.length * 0.7;
|
|
2716
|
+
if (pointer.type === "spear") return pointer.length * 0.68;
|
|
2717
|
+
return pointer.length * 0.86;
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2720
|
+
// src/layers/readout.js
|
|
2721
|
+
var readoutLayer = {
|
|
2722
|
+
id: "readout",
|
|
2723
|
+
render(context) {
|
|
2724
|
+
const { options, renderer } = context;
|
|
2725
|
+
const readout = options.readout;
|
|
2726
|
+
if (!readout.visible) return null;
|
|
2727
|
+
const group = renderer.group("readout");
|
|
2728
|
+
if (readout.title.visible && readout.title.text) {
|
|
2729
|
+
group.append(textNode("title", readout.title, readout.title.text, options));
|
|
2730
|
+
}
|
|
2731
|
+
let valueNode = null;
|
|
2732
|
+
const valueFormatter = createValueFormatter(readout.value, options);
|
|
2733
|
+
if (readout.value.visible) {
|
|
2734
|
+
valueNode = textNode("value", readout.value, "", options);
|
|
2735
|
+
valueNode.setAttribute("aria-hidden", "true");
|
|
2736
|
+
group.append(valueNode);
|
|
2737
|
+
}
|
|
2738
|
+
if (readout.unit.visible && readout.unit.text) {
|
|
2739
|
+
group.append(textNode("unit", readout.unit, readout.unit.text, options));
|
|
2740
|
+
}
|
|
2741
|
+
return {
|
|
2742
|
+
update(value) {
|
|
2743
|
+
if (valueNode) valueNode.textContent = valueFormatter(value);
|
|
2744
|
+
}
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
};
|
|
2748
|
+
function textNode(kind, config, value, options) {
|
|
2749
|
+
const position = resolveReadoutPosition(config, options);
|
|
2750
|
+
return svgElement("text", {
|
|
2751
|
+
class: `gaugeit__readout gaugeit__readout--${kind}`,
|
|
2752
|
+
x: position.x,
|
|
2753
|
+
y: position.y,
|
|
2754
|
+
fill: config.color,
|
|
2755
|
+
"font-family": config.fontFamily,
|
|
2756
|
+
"font-size": config.fontSize,
|
|
2757
|
+
"font-weight": config.fontWeight,
|
|
2758
|
+
"letter-spacing": config.letterSpacing,
|
|
2759
|
+
"text-anchor": "middle",
|
|
2760
|
+
"dominant-baseline": "middle"
|
|
2761
|
+
}, value);
|
|
2762
|
+
}
|
|
2763
|
+
function createValueFormatter(config, options) {
|
|
2764
|
+
if (typeof config.formatter === "function") {
|
|
2765
|
+
return (value) => String(config.formatter(value, { min: options.min, max: options.max, options }));
|
|
2766
|
+
}
|
|
2767
|
+
const formatter = new Intl.NumberFormat(config.locale, {
|
|
2768
|
+
minimumFractionDigits: config.fractionDigits,
|
|
2769
|
+
maximumFractionDigits: config.fractionDigits
|
|
2770
|
+
});
|
|
2771
|
+
return (value) => `${config.prefix || ""}${formatter.format(value)}${config.suffix || ""}`;
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
// src/layers/light.js
|
|
2775
|
+
var LIGHT_PALETTES = Object.freeze({
|
|
2776
|
+
red: { core: "#ef4444", highlight: "#fecaca", glow: "#f87171", rim: "#7f1d1d" },
|
|
2777
|
+
green: { core: "#22c55e", highlight: "#bbf7d0", glow: "#4ade80", rim: "#14532d" },
|
|
2778
|
+
blue: { core: "#3b82f6", highlight: "#bfdbfe", glow: "#60a5fa", rim: "#1e3a8a" },
|
|
2779
|
+
amber: { core: "#f59e0b", highlight: "#fde68a", glow: "#fb923c", rim: "#92400e" },
|
|
2780
|
+
yellow: { core: "#fde047", highlight: "#fef9c3", glow: "#facc15", rim: "#a16207" },
|
|
2781
|
+
orange: { core: "#f97316", highlight: "#fed7aa", glow: "#fb923c", rim: "#9a3412" },
|
|
2782
|
+
white: { core: "#f8fafc", highlight: "#ffffff", glow: "#e2e8f0", rim: "#64748b" },
|
|
2783
|
+
halogen: { core: "#fff2bf", highlight: "#fffdf0", glow: "#ffe79a", rim: "#9a7b24" },
|
|
2784
|
+
cyan: { core: "#22d3ee", highlight: "#cffafe", glow: "#67e8f9", rim: "#155e75" },
|
|
2785
|
+
brown: { core: "#a16207", highlight: "#f5d0a1", glow: "#d6a56b", rim: "#5b3b10" }
|
|
2786
|
+
});
|
|
2787
|
+
var lightLayer = {
|
|
2788
|
+
id: "light",
|
|
2789
|
+
render({ options, renderer }) {
|
|
2790
|
+
const light = options.light;
|
|
2791
|
+
if (!(light == null ? void 0 : light.visible)) return null;
|
|
2792
|
+
const { x, y } = resolveLightPosition(light, options);
|
|
2793
|
+
const palette = resolvePalette(light);
|
|
2794
|
+
const colorName = Object.prototype.hasOwnProperty.call(LIGHT_PALETTES, light.color) ? light.color : "amber";
|
|
2795
|
+
const glowFilterId = `${renderer.id}-light-glow`;
|
|
2796
|
+
const onGradientId = `${renderer.id}-light-on`;
|
|
2797
|
+
const offGradientId = `${renderer.id}-light-off`;
|
|
2798
|
+
const bezelGradientId = `${renderer.id}-light-bezel`;
|
|
2799
|
+
const defs = svgElement("defs");
|
|
2800
|
+
defs.append(
|
|
2801
|
+
createGlowFilter(glowFilterId, light.radius),
|
|
2802
|
+
createBulbGradient(onGradientId, {
|
|
2803
|
+
center: "#ffffff",
|
|
2804
|
+
highlight: palette.highlight,
|
|
2805
|
+
middle: palette.core,
|
|
2806
|
+
edge: palette.rim
|
|
2807
|
+
}),
|
|
2808
|
+
createBulbGradient(offGradientId, {
|
|
2809
|
+
center: "#fffdf7",
|
|
2810
|
+
highlight: light.offColor,
|
|
2811
|
+
middle: light.offColor,
|
|
2812
|
+
edge: light.offEdgeColor
|
|
2813
|
+
}),
|
|
2814
|
+
createBezelGradient(bezelGradientId, light)
|
|
2815
|
+
);
|
|
2816
|
+
const group = renderer.group("light", { "aria-hidden": "true" });
|
|
2817
|
+
group.append(defs);
|
|
2818
|
+
const lamp = svgElement("g", {
|
|
2819
|
+
class: `gaugeit__light gaugeit__light--${colorName}`,
|
|
2820
|
+
transform: `translate(${x} ${y})`,
|
|
2821
|
+
"data-gaugeit-light-color": colorName,
|
|
2822
|
+
"data-gaugeit-light-state": "off"
|
|
2823
|
+
});
|
|
2824
|
+
lamp.style.setProperty("--gaugeit-light-blink-duration", `${light.blinkInterval}ms`);
|
|
2825
|
+
lamp.style.setProperty("--gaugeit-light-pulse-duration", `${light.pulseInterval}ms`);
|
|
2826
|
+
lamp.style.setProperty("--gaugeit-light-opacity", String(light.opacity));
|
|
2827
|
+
const bezel = svgElement("circle", {
|
|
2828
|
+
class: "gaugeit__light-bezel",
|
|
2829
|
+
cx: 0,
|
|
2830
|
+
cy: 0,
|
|
2831
|
+
r: light.radius + Math.max(1.8, light.radius * 0.2),
|
|
2832
|
+
fill: `url(#${bezelGradientId})`
|
|
2833
|
+
});
|
|
2834
|
+
const bezelHighlight = svgElement("circle", {
|
|
2835
|
+
class: "gaugeit__light-bezel-highlight",
|
|
2836
|
+
cx: 0,
|
|
2837
|
+
cy: 0,
|
|
2838
|
+
r: light.radius + Math.max(0.75, light.radius * 0.08),
|
|
2839
|
+
fill: "none",
|
|
2840
|
+
stroke: light.bezelHighlightColor,
|
|
2841
|
+
"stroke-width": Math.max(0.55, light.radius * 0.075),
|
|
2842
|
+
"stroke-opacity": 0.65
|
|
2843
|
+
});
|
|
2844
|
+
const offVisual = createLampVisual({
|
|
2845
|
+
state: "off",
|
|
2846
|
+
radius: light.radius,
|
|
2847
|
+
gradientId: offGradientId,
|
|
2848
|
+
edgeColor: light.offEdgeColor,
|
|
2849
|
+
glowColor: null,
|
|
2850
|
+
glowFilterId: null,
|
|
2851
|
+
sparkleOpacity: 0.58,
|
|
2852
|
+
shadeOpacity: 0.14
|
|
2853
|
+
});
|
|
2854
|
+
const onVisual = createLampVisual({
|
|
2855
|
+
state: "on",
|
|
2856
|
+
radius: light.radius,
|
|
2857
|
+
gradientId: onGradientId,
|
|
2858
|
+
edgeColor: palette.rim,
|
|
2859
|
+
glowColor: palette.glow,
|
|
2860
|
+
glowFilterId,
|
|
2861
|
+
sparkleOpacity: 0.72,
|
|
2862
|
+
shadeOpacity: 0.1
|
|
2863
|
+
});
|
|
2864
|
+
lamp.append(bezel, bezelHighlight, offVisual, onVisual);
|
|
2865
|
+
group.append(lamp);
|
|
2866
|
+
return {
|
|
2867
|
+
update(value, state = {}) {
|
|
2868
|
+
var _a;
|
|
2869
|
+
const sample = resolveTriggerSample((_a = light.trigger) == null ? void 0 : _a.source, value, state);
|
|
2870
|
+
const active = lightIsActive(light, sample);
|
|
2871
|
+
const blinking = active && light.blink;
|
|
2872
|
+
const pulsing = active && light.pulse && !blinking;
|
|
2873
|
+
lamp.classList.toggle("is-on", active);
|
|
2874
|
+
lamp.classList.toggle("is-blinking", blinking);
|
|
2875
|
+
lamp.classList.toggle("is-pulsing", pulsing);
|
|
2876
|
+
lamp.classList.toggle("has-glow", light.glow);
|
|
2877
|
+
lamp.setAttribute("data-gaugeit-light-state", active ? "on" : "off");
|
|
2878
|
+
}
|
|
2879
|
+
};
|
|
2880
|
+
}
|
|
2881
|
+
};
|
|
2882
|
+
function createLampVisual({
|
|
2883
|
+
state,
|
|
2884
|
+
radius,
|
|
2885
|
+
gradientId,
|
|
2886
|
+
edgeColor,
|
|
2887
|
+
glowColor,
|
|
2888
|
+
glowFilterId,
|
|
2889
|
+
sparkleOpacity,
|
|
2890
|
+
shadeOpacity
|
|
2891
|
+
}) {
|
|
2892
|
+
const visual = svgElement("g", {
|
|
2893
|
+
class: `gaugeit__light-emission gaugeit__light-emission--${state}`,
|
|
2894
|
+
"data-gaugeit-light-visual": state
|
|
2895
|
+
});
|
|
2896
|
+
if (glowColor && glowFilterId) {
|
|
2897
|
+
visual.append(svgElement("circle", {
|
|
2898
|
+
class: "gaugeit__light-glow",
|
|
2899
|
+
cx: 0,
|
|
2900
|
+
cy: 0,
|
|
2901
|
+
r: radius * 1.82,
|
|
2902
|
+
fill: glowColor,
|
|
2903
|
+
filter: `url(#${glowFilterId})`
|
|
2904
|
+
}));
|
|
2905
|
+
}
|
|
2906
|
+
visual.append(
|
|
2907
|
+
svgElement("circle", {
|
|
2908
|
+
class: `gaugeit__light-bulb gaugeit__light-bulb--${state}`,
|
|
2909
|
+
cx: 0,
|
|
2910
|
+
cy: 0,
|
|
2911
|
+
r: radius,
|
|
2912
|
+
fill: `url(#${gradientId})`,
|
|
2913
|
+
stroke: edgeColor,
|
|
2914
|
+
"stroke-width": Math.max(0.8, radius * 0.13)
|
|
2915
|
+
}),
|
|
2916
|
+
svgElement("path", {
|
|
2917
|
+
class: `gaugeit__light-lens-shade gaugeit__light-lens-shade--${state}`,
|
|
2918
|
+
d: lowerLensShadePath(radius),
|
|
2919
|
+
fill: edgeColor,
|
|
2920
|
+
opacity: shadeOpacity
|
|
2921
|
+
}),
|
|
2922
|
+
svgElement("ellipse", {
|
|
2923
|
+
class: `gaugeit__light-sparkle gaugeit__light-sparkle--${state}`,
|
|
2924
|
+
cx: -radius * 0.29,
|
|
2925
|
+
cy: -radius * 0.35,
|
|
2926
|
+
rx: Math.max(0.8, radius * 0.36),
|
|
2927
|
+
ry: Math.max(0.6, radius * 0.2),
|
|
2928
|
+
fill: "#ffffff",
|
|
2929
|
+
opacity: sparkleOpacity
|
|
2930
|
+
})
|
|
2931
|
+
);
|
|
2932
|
+
return visual;
|
|
2933
|
+
}
|
|
2934
|
+
function resolvePalette(light) {
|
|
2935
|
+
const named = LIGHT_PALETTES[light.color] || LIGHT_PALETTES.amber;
|
|
2936
|
+
const custom = String(light.customColor || "").trim();
|
|
2937
|
+
if (!custom) return named;
|
|
2938
|
+
return {
|
|
2939
|
+
core: custom,
|
|
2940
|
+
highlight: "#ffffff",
|
|
2941
|
+
glow: custom,
|
|
2942
|
+
rim: custom
|
|
2943
|
+
};
|
|
2944
|
+
}
|
|
2945
|
+
function createGlowFilter(id, radius) {
|
|
2946
|
+
const filter = svgElement("filter", {
|
|
2947
|
+
id,
|
|
2948
|
+
x: "-120%",
|
|
2949
|
+
y: "-120%",
|
|
2950
|
+
width: "340%",
|
|
2951
|
+
height: "340%"
|
|
2952
|
+
});
|
|
2953
|
+
filter.append(svgElement("feGaussianBlur", {
|
|
2954
|
+
stdDeviation: Math.max(1.4, radius * 0.38)
|
|
2955
|
+
}));
|
|
2956
|
+
return filter;
|
|
2957
|
+
}
|
|
2958
|
+
function createBulbGradient(id, colors) {
|
|
2959
|
+
const gradient = svgElement("radialGradient", {
|
|
2960
|
+
id,
|
|
2961
|
+
cx: "31%",
|
|
2962
|
+
cy: "27%",
|
|
2963
|
+
r: "74%"
|
|
2964
|
+
});
|
|
2965
|
+
gradient.append(
|
|
2966
|
+
svgElement("stop", { offset: "0%", "stop-color": colors.center, "stop-opacity": 0.98 }),
|
|
2967
|
+
svgElement("stop", { offset: "23%", "stop-color": colors.highlight, "stop-opacity": 0.96 }),
|
|
2968
|
+
svgElement("stop", { offset: "62%", "stop-color": colors.middle, "stop-opacity": 1 }),
|
|
2969
|
+
svgElement("stop", { offset: "100%", "stop-color": colors.edge, "stop-opacity": 1 })
|
|
2970
|
+
);
|
|
2971
|
+
return gradient;
|
|
2972
|
+
}
|
|
2973
|
+
function createBezelGradient(id, light) {
|
|
2974
|
+
const gradient = svgElement("linearGradient", {
|
|
2975
|
+
id,
|
|
2976
|
+
x1: "0%",
|
|
2977
|
+
y1: "0%",
|
|
2978
|
+
x2: "100%",
|
|
2979
|
+
y2: "100%"
|
|
2980
|
+
});
|
|
2981
|
+
gradient.append(
|
|
2982
|
+
svgElement("stop", { offset: "0%", "stop-color": light.bezelHighlightColor, "stop-opacity": 0.92 }),
|
|
2983
|
+
svgElement("stop", { offset: "34%", "stop-color": light.bezelColor, "stop-opacity": 0.9 }),
|
|
2984
|
+
svgElement("stop", { offset: "72%", "stop-color": light.bezelColor, "stop-opacity": 1 }),
|
|
2985
|
+
svgElement("stop", { offset: "100%", "stop-color": "#1f2937", "stop-opacity": 0.92 })
|
|
2986
|
+
);
|
|
2987
|
+
return gradient;
|
|
2988
|
+
}
|
|
2989
|
+
function lowerLensShadePath(radius) {
|
|
2990
|
+
const r = Math.max(1, radius);
|
|
2991
|
+
return [
|
|
2992
|
+
`M ${-r * 0.86} ${r * 0.42}`,
|
|
2993
|
+
`A ${r} ${r} 0 0 0 ${r * 0.86} ${r * 0.42}`,
|
|
2994
|
+
`A ${r} ${r} 0 0 1 ${-r * 0.86} ${r * 0.42}`,
|
|
2995
|
+
"Z"
|
|
2996
|
+
].join(" ");
|
|
2997
|
+
}
|
|
2998
|
+
function resolveTriggerSample(source, displayedValue, state) {
|
|
2999
|
+
if (source === "pointer" && Number.isFinite(state.pointerValue)) return state.pointerValue;
|
|
3000
|
+
if (source === "displayed") return displayedValue;
|
|
3001
|
+
if (Number.isFinite(state.targetValue)) return state.targetValue;
|
|
3002
|
+
return displayedValue;
|
|
3003
|
+
}
|
|
3004
|
+
|
|
3005
|
+
// src/types/arc.js
|
|
3006
|
+
var arcGaugeType = {
|
|
3007
|
+
description: "A lightweight semicircular arc with zones, labels, and an animated pointer.",
|
|
3008
|
+
defaults: {
|
|
3009
|
+
geometry: {
|
|
3010
|
+
width: 320,
|
|
3011
|
+
height: 220,
|
|
3012
|
+
centerX: 160,
|
|
3013
|
+
centerY: 176,
|
|
3014
|
+
radius: 120,
|
|
3015
|
+
startAngle: 180,
|
|
3016
|
+
endAngle: 360
|
|
3017
|
+
},
|
|
3018
|
+
face: { visible: false },
|
|
3019
|
+
track: {
|
|
3020
|
+
visible: true,
|
|
3021
|
+
color: "#e5e7eb",
|
|
3022
|
+
width: 28,
|
|
3023
|
+
radius: 112,
|
|
3024
|
+
lineCap: "butt"
|
|
3025
|
+
},
|
|
3026
|
+
scale: {
|
|
3027
|
+
radius: 112,
|
|
3028
|
+
position: "inside",
|
|
3029
|
+
major: { visible: false },
|
|
3030
|
+
minor: { visible: false }
|
|
3031
|
+
},
|
|
3032
|
+
labels: {
|
|
3033
|
+
visible: true,
|
|
3034
|
+
radius: 82,
|
|
3035
|
+
fontSize: 11,
|
|
3036
|
+
fontWeight: 600
|
|
3037
|
+
},
|
|
3038
|
+
pointer: {
|
|
3039
|
+
type: "needle",
|
|
3040
|
+
length: 91,
|
|
3041
|
+
tailLength: 12,
|
|
3042
|
+
width: 8,
|
|
3043
|
+
cap: { radius: 7, stackOrder: "above" }
|
|
3044
|
+
},
|
|
3045
|
+
light: {
|
|
3046
|
+
visible: false,
|
|
3047
|
+
x: 160,
|
|
3048
|
+
y: 142,
|
|
3049
|
+
radius: 7,
|
|
3050
|
+
color: "amber"
|
|
3051
|
+
},
|
|
3052
|
+
readout: {
|
|
3053
|
+
title: { visible: false, x: "auto", y: 35 },
|
|
3054
|
+
value: { visible: false, x: 160, y: 130, fontSize: 30 },
|
|
3055
|
+
unit: { visible: false, x: 160, y: 150 }
|
|
3056
|
+
}
|
|
3057
|
+
},
|
|
3058
|
+
// SVG paint order: readout first, pointer above it, cap above the pointer.
|
|
3059
|
+
layers: [faceLayer, zonesLayer, ticksLayer, labelsLayer, lightLayer, readoutLayer, pointerLayer]
|
|
3060
|
+
};
|
|
3061
|
+
|
|
3062
|
+
// src/types/classic.js
|
|
3063
|
+
var classicGaugeType = {
|
|
3064
|
+
description: "A framed white analog instrument inspired by vintage electrical and pressure meters.",
|
|
3065
|
+
defaults: {
|
|
3066
|
+
layout: {
|
|
3067
|
+
includeGeometry: false
|
|
3068
|
+
},
|
|
3069
|
+
geometry: {
|
|
3070
|
+
width: 320,
|
|
3071
|
+
height: 260,
|
|
3072
|
+
centerX: 160,
|
|
3073
|
+
centerY: 183,
|
|
3074
|
+
radius: 124,
|
|
3075
|
+
startAngle: 195,
|
|
3076
|
+
endAngle: 345
|
|
3077
|
+
},
|
|
3078
|
+
face: {
|
|
3079
|
+
visible: true,
|
|
3080
|
+
shape: "panel",
|
|
3081
|
+
fit: "content",
|
|
3082
|
+
clipContent: true,
|
|
3083
|
+
x: 7,
|
|
3084
|
+
y: 7,
|
|
3085
|
+
width: 306,
|
|
3086
|
+
height: 246,
|
|
3087
|
+
cornerRadius: 20,
|
|
3088
|
+
color: "#fffef8",
|
|
3089
|
+
strokeColor: "#4b5563",
|
|
3090
|
+
strokeWidth: 3,
|
|
3091
|
+
innerStrokeColor: "#d1d5db",
|
|
3092
|
+
innerStrokeWidth: 1.2,
|
|
3093
|
+
shadow: true,
|
|
3094
|
+
glass: true
|
|
3095
|
+
},
|
|
3096
|
+
track: {
|
|
3097
|
+
visible: false,
|
|
3098
|
+
width: 8,
|
|
3099
|
+
radius: 119
|
|
3100
|
+
},
|
|
3101
|
+
scale: {
|
|
3102
|
+
radius: 119,
|
|
3103
|
+
position: "inside",
|
|
3104
|
+
major: {
|
|
3105
|
+
visible: true,
|
|
3106
|
+
divisions: 5,
|
|
3107
|
+
length: 17,
|
|
3108
|
+
width: 2.4,
|
|
3109
|
+
color: "#111827"
|
|
3110
|
+
},
|
|
3111
|
+
minor: {
|
|
3112
|
+
visible: true,
|
|
3113
|
+
subdivisions: 4,
|
|
3114
|
+
length: 9,
|
|
3115
|
+
width: 1,
|
|
3116
|
+
color: "#4b5563"
|
|
3117
|
+
}
|
|
3118
|
+
},
|
|
3119
|
+
labels: {
|
|
3120
|
+
visible: true,
|
|
3121
|
+
radius: 89,
|
|
3122
|
+
fontSize: 12,
|
|
3123
|
+
fontWeight: 650
|
|
3124
|
+
},
|
|
3125
|
+
pointer: {
|
|
3126
|
+
type: "spear",
|
|
3127
|
+
length: 99,
|
|
3128
|
+
tailLength: 15,
|
|
3129
|
+
width: 9,
|
|
3130
|
+
color: "#111827",
|
|
3131
|
+
cap: {
|
|
3132
|
+
visible: true,
|
|
3133
|
+
stackOrder: "above",
|
|
3134
|
+
radius: 10,
|
|
3135
|
+
color: "#20242a",
|
|
3136
|
+
strokeColor: "#d1d5db",
|
|
3137
|
+
strokeWidth: 2
|
|
3138
|
+
}
|
|
3139
|
+
},
|
|
3140
|
+
light: {
|
|
3141
|
+
visible: false,
|
|
3142
|
+
x: 112,
|
|
3143
|
+
y: 112,
|
|
3144
|
+
radius: 8,
|
|
3145
|
+
color: "amber"
|
|
3146
|
+
},
|
|
3147
|
+
readout: {
|
|
3148
|
+
title: {
|
|
3149
|
+
visible: true,
|
|
3150
|
+
text: "ANALOG",
|
|
3151
|
+
x: "auto",
|
|
3152
|
+
y: "auto",
|
|
3153
|
+
position: "top",
|
|
3154
|
+
margin: 12,
|
|
3155
|
+
offsetY: 18,
|
|
3156
|
+
fontSize: 13,
|
|
3157
|
+
letterSpacing: 1.6
|
|
3158
|
+
},
|
|
3159
|
+
value: {
|
|
3160
|
+
visible: false,
|
|
3161
|
+
x: 160,
|
|
3162
|
+
y: 224,
|
|
3163
|
+
fontSize: 24,
|
|
3164
|
+
fontWeight: 750
|
|
3165
|
+
},
|
|
3166
|
+
unit: {
|
|
3167
|
+
visible: false,
|
|
3168
|
+
x: 160,
|
|
3169
|
+
y: 242,
|
|
3170
|
+
fontSize: 10
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
},
|
|
3174
|
+
// SVG paint order: readout first, pointer above it, cap above the pointer.
|
|
3175
|
+
layers: [faceLayer, zonesLayer, ticksLayer, labelsLayer, lightLayer, readoutLayer, pointerLayer]
|
|
3176
|
+
};
|
|
3177
|
+
|
|
3178
|
+
// src/types/line-scale.js
|
|
3179
|
+
var lineScaleGaugeType = {
|
|
3180
|
+
description: "A dense line-scale gauge with independently configurable major and minor ticks.",
|
|
3181
|
+
defaults: {
|
|
3182
|
+
geometry: {
|
|
3183
|
+
width: 320,
|
|
3184
|
+
height: 225,
|
|
3185
|
+
centerX: 160,
|
|
3186
|
+
centerY: 184,
|
|
3187
|
+
radius: 127,
|
|
3188
|
+
startAngle: 180,
|
|
3189
|
+
endAngle: 360
|
|
3190
|
+
},
|
|
3191
|
+
face: { visible: false },
|
|
3192
|
+
track: {
|
|
3193
|
+
visible: true,
|
|
3194
|
+
color: "#cbd5e1",
|
|
3195
|
+
width: 2,
|
|
3196
|
+
radius: 126,
|
|
3197
|
+
lineCap: "round"
|
|
3198
|
+
},
|
|
3199
|
+
scale: {
|
|
3200
|
+
radius: 124,
|
|
3201
|
+
position: "inside",
|
|
3202
|
+
major: {
|
|
3203
|
+
visible: true,
|
|
3204
|
+
divisions: 8,
|
|
3205
|
+
length: 23,
|
|
3206
|
+
width: 2.4,
|
|
3207
|
+
color: "#0f172a"
|
|
3208
|
+
},
|
|
3209
|
+
minor: {
|
|
3210
|
+
visible: true,
|
|
3211
|
+
subdivisions: 4,
|
|
3212
|
+
length: 11,
|
|
3213
|
+
width: 1,
|
|
3214
|
+
color: "#475569"
|
|
3215
|
+
}
|
|
3216
|
+
},
|
|
3217
|
+
labels: {
|
|
3218
|
+
visible: true,
|
|
3219
|
+
radius: 88,
|
|
3220
|
+
fontSize: 10,
|
|
3221
|
+
fontWeight: 650
|
|
3222
|
+
},
|
|
3223
|
+
pointer: {
|
|
3224
|
+
type: "line",
|
|
3225
|
+
length: 101,
|
|
3226
|
+
tailLength: 10,
|
|
3227
|
+
width: 4,
|
|
3228
|
+
color: "#b91c1c",
|
|
3229
|
+
cap: {
|
|
3230
|
+
visible: true,
|
|
3231
|
+
stackOrder: "above",
|
|
3232
|
+
radius: 7,
|
|
3233
|
+
color: "#b91c1c",
|
|
3234
|
+
strokeColor: "#ffffff",
|
|
3235
|
+
strokeWidth: 2
|
|
3236
|
+
}
|
|
3237
|
+
},
|
|
3238
|
+
light: {
|
|
3239
|
+
visible: false,
|
|
3240
|
+
x: 160,
|
|
3241
|
+
y: 151,
|
|
3242
|
+
radius: 7,
|
|
3243
|
+
color: "amber"
|
|
3244
|
+
},
|
|
3245
|
+
readout: {
|
|
3246
|
+
title: { visible: false, x: "auto", y: 140 },
|
|
3247
|
+
value: { visible: false, x: 160, y: 220, fontSize: 33 },
|
|
3248
|
+
unit: { visible: false, x: 160, y: 240 }
|
|
3249
|
+
}
|
|
3250
|
+
},
|
|
3251
|
+
// SVG paint order: readout first, pointer above it, cap above the pointer.
|
|
3252
|
+
layers: [faceLayer, zonesLayer, ticksLayer, labelsLayer, lightLayer, readoutLayer, pointerLayer]
|
|
3253
|
+
};
|
|
3254
|
+
|
|
3255
|
+
// src/types/heritage-round.js
|
|
3256
|
+
var heritageRoundGaugeType = {
|
|
3257
|
+
description: "A circular, glass-fronted vintage instrument with a 270-degree scale.",
|
|
3258
|
+
defaults: {
|
|
3259
|
+
geometry: {
|
|
3260
|
+
width: 320,
|
|
3261
|
+
height: 320,
|
|
3262
|
+
centerX: 160,
|
|
3263
|
+
centerY: 160,
|
|
3264
|
+
radius: 132,
|
|
3265
|
+
startAngle: 225,
|
|
3266
|
+
endAngle: 495
|
|
3267
|
+
},
|
|
3268
|
+
face: {
|
|
3269
|
+
visible: true,
|
|
3270
|
+
shape: "circle",
|
|
3271
|
+
fit: "geometry",
|
|
3272
|
+
clipContent: true,
|
|
3273
|
+
x: 8,
|
|
3274
|
+
y: 8,
|
|
3275
|
+
width: 304,
|
|
3276
|
+
height: 304,
|
|
3277
|
+
cornerRadius: 0,
|
|
3278
|
+
color: "#fffdf5",
|
|
3279
|
+
strokeColor: "#374151",
|
|
3280
|
+
strokeWidth: 4,
|
|
3281
|
+
innerStrokeColor: "#c7bfae",
|
|
3282
|
+
innerStrokeWidth: 1.4,
|
|
3283
|
+
shadow: true,
|
|
3284
|
+
glass: true
|
|
3285
|
+
},
|
|
3286
|
+
track: {
|
|
3287
|
+
visible: false,
|
|
3288
|
+
width: 7,
|
|
3289
|
+
radius: 124
|
|
3290
|
+
},
|
|
3291
|
+
scale: {
|
|
3292
|
+
endpointMode: "auto",
|
|
3293
|
+
radius: 124,
|
|
3294
|
+
position: "inside",
|
|
3295
|
+
major: {
|
|
3296
|
+
visible: true,
|
|
3297
|
+
divisions: 9,
|
|
3298
|
+
length: 18,
|
|
3299
|
+
width: 2.6,
|
|
3300
|
+
color: "#171717"
|
|
3301
|
+
},
|
|
3302
|
+
minor: {
|
|
3303
|
+
visible: true,
|
|
3304
|
+
subdivisions: 4,
|
|
3305
|
+
length: 9,
|
|
3306
|
+
width: 1,
|
|
3307
|
+
color: "#57534e"
|
|
3308
|
+
}
|
|
3309
|
+
},
|
|
3310
|
+
labels: {
|
|
3311
|
+
visible: true,
|
|
3312
|
+
radius: 92,
|
|
3313
|
+
fontSize: 11,
|
|
3314
|
+
fontWeight: 700
|
|
3315
|
+
},
|
|
3316
|
+
pointer: {
|
|
3317
|
+
type: "spear",
|
|
3318
|
+
length: 104,
|
|
3319
|
+
tailLength: 18,
|
|
3320
|
+
width: 9,
|
|
3321
|
+
color: "#7f1d1d",
|
|
3322
|
+
cap: {
|
|
3323
|
+
visible: true,
|
|
3324
|
+
stackOrder: "above",
|
|
3325
|
+
radius: 11,
|
|
3326
|
+
color: "#292524",
|
|
3327
|
+
strokeColor: "#d6d3d1",
|
|
3328
|
+
strokeWidth: 2
|
|
3329
|
+
}
|
|
3330
|
+
},
|
|
3331
|
+
light: {
|
|
3332
|
+
visible: false,
|
|
3333
|
+
x: 108,
|
|
3334
|
+
y: 110,
|
|
3335
|
+
radius: 10,
|
|
3336
|
+
color: "amber",
|
|
3337
|
+
blink: false,
|
|
3338
|
+
blinkInterval: 1e3,
|
|
3339
|
+
trigger: { mode: "below", source: "target", value: 15, min: 0, max: 15 }
|
|
3340
|
+
},
|
|
3341
|
+
readout: {
|
|
3342
|
+
title: {
|
|
3343
|
+
visible: true,
|
|
3344
|
+
text: "HERITAGE",
|
|
3345
|
+
x: "auto",
|
|
3346
|
+
y: "auto",
|
|
3347
|
+
position: "top",
|
|
3348
|
+
margin: 14,
|
|
3349
|
+
offsetY: 85,
|
|
3350
|
+
fontSize: 13,
|
|
3351
|
+
letterSpacing: 1.8
|
|
3352
|
+
},
|
|
3353
|
+
value: {
|
|
3354
|
+
visible: false,
|
|
3355
|
+
x: "auto",
|
|
3356
|
+
y: 224,
|
|
3357
|
+
fontSize: 25,
|
|
3358
|
+
fontWeight: 750
|
|
3359
|
+
},
|
|
3360
|
+
unit: {
|
|
3361
|
+
visible: false,
|
|
3362
|
+
x: "auto",
|
|
3363
|
+
y: 244,
|
|
3364
|
+
fontSize: 10
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
},
|
|
3368
|
+
// SVG paint order: readout first, pointer above it, cap above the pointer.
|
|
3369
|
+
layers: [faceLayer, zonesLayer, ticksLayer, labelsLayer, lightLayer, readoutLayer, pointerLayer]
|
|
3370
|
+
};
|
|
3371
|
+
|
|
3372
|
+
// src/layers/linear-zones.js
|
|
3373
|
+
var linearZonesLayer = {
|
|
3374
|
+
id: "linear-zones",
|
|
3375
|
+
render(context) {
|
|
3376
|
+
const { options, renderer } = context;
|
|
3377
|
+
const group = renderer.group("linear-zones", { "aria-hidden": "true" });
|
|
3378
|
+
const segments = resolveZoneSegments(options.min, options.max, options.zones);
|
|
3379
|
+
if (options.track.showUnderZones && options.track.visible) {
|
|
3380
|
+
group.append(createSegment(options.min, options.max, options.track, "gaugeit__track"));
|
|
3381
|
+
}
|
|
3382
|
+
for (const segment of segments) {
|
|
3383
|
+
const style = segment.zone || options.track;
|
|
3384
|
+
const shouldDraw = segment.zone ? !isTransparentColor(segment.zone.color) : options.track.visible && !options.track.showUnderZones;
|
|
3385
|
+
if (!shouldDraw) continue;
|
|
3386
|
+
const className = segment.zone ? "gaugeit__zone" : "gaugeit__track";
|
|
3387
|
+
const line = createSegment(segment.min, segment.max, style, className);
|
|
3388
|
+
if (segment.zone && segment.zone.className) line.classList.add(String(segment.zone.className));
|
|
3389
|
+
group.append(line);
|
|
3390
|
+
}
|
|
3391
|
+
return null;
|
|
3392
|
+
function createSegment(min, max, style, className) {
|
|
3393
|
+
var _a, _b, _c, _d;
|
|
3394
|
+
const axis = linearAxis(options);
|
|
3395
|
+
const offset = linearNormalOffset((_a = style.offset) != null ? _a : options.linear.zoneOffset);
|
|
3396
|
+
const start = offsetLinearPoint(valueToLinearPoint(min, options), axis, offset);
|
|
3397
|
+
const end = offsetLinearPoint(valueToLinearPoint(max, options), axis, offset);
|
|
3398
|
+
return svgElement("line", {
|
|
3399
|
+
class: className,
|
|
3400
|
+
x1: start.x,
|
|
3401
|
+
y1: start.y,
|
|
3402
|
+
x2: end.x,
|
|
3403
|
+
y2: end.y,
|
|
3404
|
+
stroke: (_b = style.color) != null ? _b : options.track.color,
|
|
3405
|
+
"stroke-width": (_c = style.width) != null ? _c : options.track.width,
|
|
3406
|
+
"stroke-linecap": style.lineCap || options.track.lineCap || "butt",
|
|
3407
|
+
opacity: (_d = style.opacity) != null ? _d : 1
|
|
3408
|
+
});
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
};
|
|
3412
|
+
|
|
3413
|
+
// src/layers/linear-ticks.js
|
|
3414
|
+
var linearTicksLayer = {
|
|
3415
|
+
id: "linear-ticks",
|
|
3416
|
+
render(context) {
|
|
3417
|
+
const { options, renderer } = context;
|
|
3418
|
+
const scale = options.scale;
|
|
3419
|
+
if (!scale.major.visible && !scale.minor.visible) return null;
|
|
3420
|
+
const group = renderer.group("linear-ticks", { "aria-hidden": "true" });
|
|
3421
|
+
const interval = resolveMajorInterval(options);
|
|
3422
|
+
const majorValues = buildScaleValues(options.min, options.max, interval);
|
|
3423
|
+
const minorValues = buildMinorValues(majorValues, scale.minor.subdivisions);
|
|
3424
|
+
if (scale.minor.visible && scale.minor.length > 0 && scale.minor.width > 0 && minorValues.length) {
|
|
3425
|
+
group.append(svgElement("path", {
|
|
3426
|
+
class: "gaugeit__ticks gaugeit__ticks--minor gaugeit__linear-ticks",
|
|
3427
|
+
d: tickPath(minorValues, scale.minor.length),
|
|
3428
|
+
fill: "none",
|
|
3429
|
+
stroke: scale.minor.color,
|
|
3430
|
+
"stroke-width": scale.minor.width,
|
|
3431
|
+
"stroke-linecap": "butt",
|
|
3432
|
+
opacity: scale.minor.opacity
|
|
3433
|
+
}));
|
|
3434
|
+
}
|
|
3435
|
+
if (scale.major.visible && scale.major.length > 0 && scale.major.width > 0) {
|
|
3436
|
+
group.append(svgElement("path", {
|
|
3437
|
+
class: "gaugeit__ticks gaugeit__ticks--major gaugeit__linear-ticks",
|
|
3438
|
+
d: tickPath(majorValues, scale.major.length),
|
|
3439
|
+
fill: "none",
|
|
3440
|
+
stroke: scale.major.color,
|
|
3441
|
+
"stroke-width": scale.major.width,
|
|
3442
|
+
"stroke-linecap": "butt",
|
|
3443
|
+
opacity: scale.major.opacity
|
|
3444
|
+
}));
|
|
3445
|
+
}
|
|
3446
|
+
renderer.setReference("majorValues", majorValues);
|
|
3447
|
+
return null;
|
|
3448
|
+
function tickPath(values, length) {
|
|
3449
|
+
const axis = linearAxis(options);
|
|
3450
|
+
const span = linearNormalSpan(length, options.linear.tickSide);
|
|
3451
|
+
const commands = [];
|
|
3452
|
+
for (const value of values) {
|
|
3453
|
+
const point = valueToLinearPoint(value, options);
|
|
3454
|
+
const start = offsetLinearPoint(point, axis, span.start);
|
|
3455
|
+
const end = offsetLinearPoint(point, axis, span.end);
|
|
3456
|
+
commands.push(`M ${roundSvg(start.x)} ${roundSvg(start.y)} L ${roundSvg(end.x)} ${roundSvg(end.y)}`);
|
|
3457
|
+
}
|
|
3458
|
+
return commands.join(" ");
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
};
|
|
3462
|
+
|
|
3463
|
+
// src/layers/linear-labels.js
|
|
3464
|
+
var linearLabelsLayer = {
|
|
3465
|
+
id: "linear-labels",
|
|
3466
|
+
render(context) {
|
|
3467
|
+
const { options, renderer } = context;
|
|
3468
|
+
const labels = options.labels;
|
|
3469
|
+
if (!labels.visible) return null;
|
|
3470
|
+
const group = renderer.group("linear-labels", { "aria-hidden": "true" });
|
|
3471
|
+
const interval = Number(labels.interval) > 0 ? Number(labels.interval) : resolveMajorInterval(options);
|
|
3472
|
+
const values = buildScaleValues(options.min, options.max, interval);
|
|
3473
|
+
const axis = linearAxis(options);
|
|
3474
|
+
for (const value of values) {
|
|
3475
|
+
const point = offsetLinearPoint(
|
|
3476
|
+
valueToLinearPoint(value, options),
|
|
3477
|
+
axis,
|
|
3478
|
+
linearNormalOffset(options.linear.labelOffset)
|
|
3479
|
+
);
|
|
3480
|
+
const text = svgElement("text", {
|
|
3481
|
+
class: "gaugeit__label gaugeit__linear-label",
|
|
3482
|
+
x: point.x,
|
|
3483
|
+
y: point.y,
|
|
3484
|
+
fill: labels.color,
|
|
3485
|
+
"font-family": labels.fontFamily,
|
|
3486
|
+
"font-size": labels.fontSize,
|
|
3487
|
+
"font-weight": labels.fontWeight,
|
|
3488
|
+
"text-anchor": "middle",
|
|
3489
|
+
"dominant-baseline": "middle",
|
|
3490
|
+
transform: labelTransform2(labels.rotation, axis.angle, point)
|
|
3491
|
+
}, formatLabel2(value, labels, options));
|
|
3492
|
+
group.append(text);
|
|
3493
|
+
}
|
|
3494
|
+
return null;
|
|
3495
|
+
}
|
|
3496
|
+
};
|
|
3497
|
+
function labelTransform2(rotation, axisAngle, point) {
|
|
3498
|
+
if (rotation === "radial") return `rotate(${axisAngle + 90} ${point.x} ${point.y})`;
|
|
3499
|
+
if (rotation === "tangent") return `rotate(${axisAngle} ${point.x} ${point.y})`;
|
|
3500
|
+
return void 0;
|
|
3501
|
+
}
|
|
3502
|
+
function formatLabel2(value, labels, options) {
|
|
3503
|
+
if (typeof labels.formatter === "function") {
|
|
3504
|
+
return labels.formatter(value, { min: options.min, max: options.max, options });
|
|
3505
|
+
}
|
|
3506
|
+
return Number(value).toFixed(labels.fractionDigits);
|
|
3507
|
+
}
|
|
3508
|
+
|
|
3509
|
+
// src/layers/linear-indicator.js
|
|
3510
|
+
var linearIndicatorLayer = {
|
|
3511
|
+
id: "linear-indicator",
|
|
3512
|
+
render(context) {
|
|
3513
|
+
const { options, renderer } = context;
|
|
3514
|
+
const indicator = options.indicator;
|
|
3515
|
+
if (!indicator.visible) return null;
|
|
3516
|
+
const axis = linearAxis(options);
|
|
3517
|
+
const normalOffset = linearIndicatorNormalOffset(indicator);
|
|
3518
|
+
const group = renderer.group("linear-indicator", { "aria-hidden": "true" });
|
|
3519
|
+
const moving = svgElement("g", {
|
|
3520
|
+
class: "gaugeit__linear-indicator-moving"
|
|
3521
|
+
});
|
|
3522
|
+
const shape = createIndicatorShape(indicator, normalOffset);
|
|
3523
|
+
moving.append(shape);
|
|
3524
|
+
let cap = null;
|
|
3525
|
+
if (indicator.cap.visible) {
|
|
3526
|
+
cap = svgElement("circle", {
|
|
3527
|
+
class: "gaugeit__linear-indicator-cap",
|
|
3528
|
+
cx: 0,
|
|
3529
|
+
cy: normalOffset,
|
|
3530
|
+
r: indicator.cap.radius,
|
|
3531
|
+
fill: indicator.cap.color,
|
|
3532
|
+
stroke: indicator.cap.strokeColor,
|
|
3533
|
+
"stroke-width": indicator.cap.strokeWidth
|
|
3534
|
+
});
|
|
3535
|
+
if (indicator.cap.stackOrder === "below") moving.prepend(cap);
|
|
3536
|
+
else moving.append(cap);
|
|
3537
|
+
}
|
|
3538
|
+
group.append(moving);
|
|
3539
|
+
return {
|
|
3540
|
+
update(value, state = {}) {
|
|
3541
|
+
const visualValue = Number.isFinite(state.pointerValue) ? state.pointerValue : value;
|
|
3542
|
+
const point = valueToLinearPoint(visualValue, options, options.overflow !== "allow");
|
|
3543
|
+
moving.setAttribute("transform", `translate(${point.x} ${point.y}) rotate(${axis.angle})`);
|
|
3544
|
+
if (indicator.colorByZone) {
|
|
3545
|
+
const zone = findZoneForValue(visualValue, options.zones);
|
|
3546
|
+
const color = zone && !isTransparentColor(zone.color) ? zone.color : indicator.color;
|
|
3547
|
+
setIndicatorColor(shape, indicator, color);
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
};
|
|
3551
|
+
}
|
|
3552
|
+
};
|
|
3553
|
+
function createIndicatorShape(indicator, normalOffset) {
|
|
3554
|
+
const span = linearNormalSpan(indicator.length, indicator.position, normalOffset);
|
|
3555
|
+
let shape;
|
|
3556
|
+
if (indicator.type === "marker") {
|
|
3557
|
+
const direction = markerDirection(indicator.position);
|
|
3558
|
+
if (direction === 0) {
|
|
3559
|
+
const size = indicator.markerSize;
|
|
3560
|
+
const centerY = normalOffset;
|
|
3561
|
+
shape = svgElement("path", {
|
|
3562
|
+
class: "gaugeit__linear-indicator gaugeit__linear-indicator--marker",
|
|
3563
|
+
d: `M 0 ${centerY - size} L ${size * 0.72} ${centerY} L 0 ${centerY + size} L ${-size * 0.72} ${centerY} Z`,
|
|
3564
|
+
fill: indicator.color
|
|
3565
|
+
});
|
|
3566
|
+
} else {
|
|
3567
|
+
const tipY = normalOffset;
|
|
3568
|
+
const baseY = tipY + direction * indicator.markerSize;
|
|
3569
|
+
const halfWidth = indicator.markerSize * 0.72;
|
|
3570
|
+
shape = svgElement("path", {
|
|
3571
|
+
class: "gaugeit__linear-indicator gaugeit__linear-indicator--marker",
|
|
3572
|
+
d: `M 0 ${tipY} L ${halfWidth} ${baseY} L ${-halfWidth} ${baseY} Z`,
|
|
3573
|
+
fill: indicator.color
|
|
3574
|
+
});
|
|
3575
|
+
}
|
|
3576
|
+
} else if (indicator.type === "carriage") {
|
|
3577
|
+
const y = Math.min(span.start, span.end);
|
|
3578
|
+
shape = svgElement("rect", {
|
|
3579
|
+
class: "gaugeit__linear-indicator gaugeit__linear-indicator--carriage",
|
|
3580
|
+
x: -indicator.carriageWidth / 2,
|
|
3581
|
+
y,
|
|
3582
|
+
width: indicator.carriageWidth,
|
|
3583
|
+
height: Math.max(1, Math.abs(span.end - span.start)),
|
|
3584
|
+
rx: indicator.carriageRadius,
|
|
3585
|
+
ry: indicator.carriageRadius,
|
|
3586
|
+
fill: indicator.color
|
|
3587
|
+
});
|
|
3588
|
+
} else {
|
|
3589
|
+
shape = svgElement("line", {
|
|
3590
|
+
class: "gaugeit__linear-indicator gaugeit__linear-indicator--hairline",
|
|
3591
|
+
x1: 0,
|
|
3592
|
+
y1: span.start,
|
|
3593
|
+
x2: 0,
|
|
3594
|
+
y2: span.end,
|
|
3595
|
+
stroke: indicator.color,
|
|
3596
|
+
"stroke-width": indicator.width,
|
|
3597
|
+
"stroke-linecap": indicator.lineCap
|
|
3598
|
+
});
|
|
3599
|
+
}
|
|
3600
|
+
setSvgAttributes(shape, {
|
|
3601
|
+
opacity: indicator.opacity,
|
|
3602
|
+
stroke: indicator.type === "hairline" ? indicator.color : indicator.strokeColor,
|
|
3603
|
+
"stroke-width": indicator.type === "hairline" ? indicator.width : indicator.strokeWidth
|
|
3604
|
+
});
|
|
3605
|
+
return shape;
|
|
3606
|
+
}
|
|
3607
|
+
function markerDirection(position) {
|
|
3608
|
+
if (position === "negative") return -1;
|
|
3609
|
+
if (position === "positive") return 1;
|
|
3610
|
+
return 0;
|
|
3611
|
+
}
|
|
3612
|
+
function setIndicatorColor(shape, indicator, color) {
|
|
3613
|
+
if (indicator.type === "hairline") shape.setAttribute("stroke", color);
|
|
3614
|
+
else shape.setAttribute("fill", color);
|
|
3615
|
+
}
|
|
3616
|
+
|
|
3617
|
+
// src/types/instrument-sizing.js
|
|
3618
|
+
function resolveIntrinsicGeometrySize(options, context) {
|
|
3619
|
+
var _a, _b;
|
|
3620
|
+
const requestedGeometry = isPlainObject((_a = context.requested) == null ? void 0 : _a.geometry) ? context.requested.geometry : {};
|
|
3621
|
+
const typeGeometry = ((_b = context.typeDefaults) == null ? void 0 : _b.geometry) || {};
|
|
3622
|
+
const widthExplicit = hasOwn(requestedGeometry, "width");
|
|
3623
|
+
const heightExplicit = hasOwn(requestedGeometry, "height");
|
|
3624
|
+
const ratioExplicit = hasOwn(requestedGeometry, "aspectRatio");
|
|
3625
|
+
const ratio = finiteNumber(requestedGeometry.aspectRatio, 0);
|
|
3626
|
+
if (ratioExplicit && ratio > 0) {
|
|
3627
|
+
if (widthExplicit && !heightExplicit) options.geometry.height = options.geometry.width / ratio;
|
|
3628
|
+
else if (heightExplicit && !widthExplicit) options.geometry.width = options.geometry.height * ratio;
|
|
3629
|
+
else if (!widthExplicit && !heightExplicit) {
|
|
3630
|
+
const baseWidth = finiteNumber(typeGeometry.width, options.geometry.width);
|
|
3631
|
+
options.geometry.width = baseWidth;
|
|
3632
|
+
options.geometry.height = baseWidth / ratio;
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
options.geometry.aspectRatio = ratioExplicit && ratio > 0 ? ratio : null;
|
|
3636
|
+
return {
|
|
3637
|
+
requestedGeometry,
|
|
3638
|
+
widthExplicit: widthExplicit || ratioExplicit && ratio > 0 && !heightExplicit,
|
|
3639
|
+
heightExplicit: heightExplicit || ratioExplicit && ratio > 0,
|
|
3640
|
+
baseWidth: Math.max(1, finiteNumber(typeGeometry.width, options.geometry.width)),
|
|
3641
|
+
baseHeight: Math.max(1, finiteNumber(typeGeometry.height, options.geometry.height))
|
|
3642
|
+
};
|
|
3643
|
+
}
|
|
3644
|
+
function configureClassicLinearSize(options, context) {
|
|
3645
|
+
const sizing = resolveIntrinsicGeometrySize(options, context);
|
|
3646
|
+
if (!sizing.widthExplicit && !sizing.heightExplicit) return options;
|
|
3647
|
+
const scaleX = options.geometry.width / sizing.baseWidth;
|
|
3648
|
+
const scaleY = options.geometry.height / sizing.baseHeight;
|
|
3649
|
+
const uniform = Math.min(scaleX, scaleY);
|
|
3650
|
+
scalePaths(options, context, scaleX, [
|
|
3651
|
+
"geometry.centerX",
|
|
3652
|
+
"face.x",
|
|
3653
|
+
"face.width",
|
|
3654
|
+
"face.minWidth",
|
|
3655
|
+
"linear.startX",
|
|
3656
|
+
"linear.endX",
|
|
3657
|
+
"readout.title.x",
|
|
3658
|
+
"readout.value.x",
|
|
3659
|
+
"readout.unit.x"
|
|
3660
|
+
]);
|
|
3661
|
+
scalePaths(options, context, scaleY, [
|
|
3662
|
+
"geometry.centerY",
|
|
3663
|
+
"face.y",
|
|
3664
|
+
"face.height",
|
|
3665
|
+
"face.minHeight",
|
|
3666
|
+
"linear.originY",
|
|
3667
|
+
"linear.startY",
|
|
3668
|
+
"linear.endY",
|
|
3669
|
+
"linear.labelOffset",
|
|
3670
|
+
"linear.zoneOffset",
|
|
3671
|
+
"scale.major.length",
|
|
3672
|
+
"scale.minor.length",
|
|
3673
|
+
"indicator.length",
|
|
3674
|
+
"indicator.centerOffset",
|
|
3675
|
+
"indicator.markerSize",
|
|
3676
|
+
"indicator.carriageWidth",
|
|
3677
|
+
"indicator.carriageRadius",
|
|
3678
|
+
"indicator.cap.radius",
|
|
3679
|
+
"centerZero.marker.length",
|
|
3680
|
+
"centerZero.marker.centerOffset",
|
|
3681
|
+
"readout.title.y",
|
|
3682
|
+
"readout.value.y",
|
|
3683
|
+
"readout.unit.y"
|
|
3684
|
+
]);
|
|
3685
|
+
scalePaths(options, context, uniform, [
|
|
3686
|
+
"face.contentPadding",
|
|
3687
|
+
"face.cornerRadius"
|
|
3688
|
+
]);
|
|
3689
|
+
return options;
|
|
3690
|
+
}
|
|
3691
|
+
function configureRollingTapeSize(options, context) {
|
|
3692
|
+
const sizing = resolveIntrinsicGeometrySize(options, context);
|
|
3693
|
+
if (!sizing.widthExplicit && !sizing.heightExplicit) return options;
|
|
3694
|
+
const scaleX = options.geometry.width / sizing.baseWidth;
|
|
3695
|
+
const scaleY = options.geometry.height / sizing.baseHeight;
|
|
3696
|
+
const uniform = Math.min(scaleX, scaleY);
|
|
3697
|
+
const vertical = options.tape.orientation !== "horizontal";
|
|
3698
|
+
scalePaths(options, context, scaleX, [
|
|
3699
|
+
"geometry.centerX",
|
|
3700
|
+
"face.x",
|
|
3701
|
+
"face.width",
|
|
3702
|
+
"face.minWidth",
|
|
3703
|
+
"tape.x",
|
|
3704
|
+
"tape.width",
|
|
3705
|
+
"readout.title.x",
|
|
3706
|
+
"readout.value.x",
|
|
3707
|
+
"readout.unit.x"
|
|
3708
|
+
]);
|
|
3709
|
+
scalePaths(options, context, scaleY, [
|
|
3710
|
+
"geometry.centerY",
|
|
3711
|
+
"face.y",
|
|
3712
|
+
"face.height",
|
|
3713
|
+
"face.minHeight",
|
|
3714
|
+
"tape.y",
|
|
3715
|
+
"tape.height",
|
|
3716
|
+
"readout.title.y",
|
|
3717
|
+
"readout.value.y",
|
|
3718
|
+
"readout.unit.y"
|
|
3719
|
+
]);
|
|
3720
|
+
scalePaths(options, context, vertical ? scaleX : scaleY, [
|
|
3721
|
+
"tape.tickInset",
|
|
3722
|
+
"tape.labelOffset",
|
|
3723
|
+
"tape.majorTickLength",
|
|
3724
|
+
"tape.minorTickLength"
|
|
3725
|
+
]);
|
|
3726
|
+
scalePaths(options, context, vertical ? scaleY : scaleX, [
|
|
3727
|
+
"tape.majorSpacing",
|
|
3728
|
+
"tape.fadeSize"
|
|
3729
|
+
]);
|
|
3730
|
+
scalePaths(options, context, uniform, [
|
|
3731
|
+
"face.cornerRadius",
|
|
3732
|
+
"tape.cornerRadius"
|
|
3733
|
+
]);
|
|
3734
|
+
return options;
|
|
3735
|
+
}
|
|
3736
|
+
function scalePaths(options, context, factor, paths) {
|
|
3737
|
+
if (!Number.isFinite(factor) || factor <= 0 || Math.abs(factor - 1) < 1e-9) return;
|
|
3738
|
+
for (const path of paths) {
|
|
3739
|
+
if (hasPath(context.requested, path)) continue;
|
|
3740
|
+
const baseValue = getPath(context.typeDefaults, path);
|
|
3741
|
+
if (!Number.isFinite(baseValue)) continue;
|
|
3742
|
+
setPath(options, path, baseValue * factor);
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
function hasOwn(value, key) {
|
|
3746
|
+
return isPlainObject(value) && Object.prototype.hasOwnProperty.call(value, key);
|
|
3747
|
+
}
|
|
3748
|
+
function hasPath(value, path) {
|
|
3749
|
+
const keys = path.split(".");
|
|
3750
|
+
let cursor = value;
|
|
3751
|
+
for (const key of keys) {
|
|
3752
|
+
if (!isPlainObject(cursor) || !Object.prototype.hasOwnProperty.call(cursor, key)) return false;
|
|
3753
|
+
cursor = cursor[key];
|
|
3754
|
+
}
|
|
3755
|
+
return true;
|
|
3756
|
+
}
|
|
3757
|
+
function getPath(value, path) {
|
|
3758
|
+
let cursor = value;
|
|
3759
|
+
for (const key of path.split(".")) {
|
|
3760
|
+
if (cursor === null || cursor === void 0) return void 0;
|
|
3761
|
+
cursor = cursor[key];
|
|
3762
|
+
}
|
|
3763
|
+
return cursor;
|
|
3764
|
+
}
|
|
3765
|
+
function setPath(value, path, nextValue) {
|
|
3766
|
+
const keys = path.split(".");
|
|
3767
|
+
let cursor = value;
|
|
3768
|
+
for (let index = 0; index < keys.length - 1; index += 1) {
|
|
3769
|
+
const key = keys[index];
|
|
3770
|
+
if (!isPlainObject(cursor[key])) cursor[key] = {};
|
|
3771
|
+
cursor = cursor[key];
|
|
3772
|
+
}
|
|
3773
|
+
cursor[keys[keys.length - 1]] = nextValue;
|
|
3774
|
+
}
|
|
3775
|
+
|
|
3776
|
+
// src/types/classic-linear.js
|
|
3777
|
+
var classicLinearDefaults = {
|
|
3778
|
+
layout: {
|
|
3779
|
+
includeGeometry: false
|
|
3780
|
+
},
|
|
3781
|
+
geometry: {
|
|
3782
|
+
mode: "linear",
|
|
3783
|
+
width: 320,
|
|
3784
|
+
height: 220,
|
|
3785
|
+
centerX: 160,
|
|
3786
|
+
centerY: 110
|
|
3787
|
+
},
|
|
3788
|
+
face: {
|
|
3789
|
+
visible: true,
|
|
3790
|
+
shape: "panel",
|
|
3791
|
+
fit: "content",
|
|
3792
|
+
contentPadding: 14,
|
|
3793
|
+
minWidth: 1,
|
|
3794
|
+
minHeight: 1,
|
|
3795
|
+
clipContent: true,
|
|
3796
|
+
x: 7,
|
|
3797
|
+
y: 7,
|
|
3798
|
+
width: 306,
|
|
3799
|
+
height: 206,
|
|
3800
|
+
cornerRadius: 20,
|
|
3801
|
+
color: "#fffef8",
|
|
3802
|
+
strokeColor: "#4b5563",
|
|
3803
|
+
strokeWidth: 3,
|
|
3804
|
+
innerStrokeColor: "#d1d5db",
|
|
3805
|
+
innerStrokeWidth: 1.2,
|
|
3806
|
+
shadow: true,
|
|
3807
|
+
glass: true
|
|
3808
|
+
},
|
|
3809
|
+
linear: {
|
|
3810
|
+
originY: 142,
|
|
3811
|
+
startX: 38,
|
|
3812
|
+
startY: 0,
|
|
3813
|
+
endX: 282,
|
|
3814
|
+
endY: 0,
|
|
3815
|
+
tickSide: "negative",
|
|
3816
|
+
labelOffset: 38,
|
|
3817
|
+
zoneOffset: -16
|
|
3818
|
+
},
|
|
3819
|
+
track: {
|
|
3820
|
+
visible: true,
|
|
3821
|
+
color: "#a8a29e",
|
|
3822
|
+
width: 5,
|
|
3823
|
+
opacity: 0.85,
|
|
3824
|
+
lineCap: "round",
|
|
3825
|
+
showUnderZones: false
|
|
3826
|
+
},
|
|
3827
|
+
scale: {
|
|
3828
|
+
major: {
|
|
3829
|
+
visible: true,
|
|
3830
|
+
divisions: 5,
|
|
3831
|
+
length: 20,
|
|
3832
|
+
width: 2.4,
|
|
3833
|
+
color: "#111827"
|
|
3834
|
+
},
|
|
3835
|
+
minor: {
|
|
3836
|
+
visible: true,
|
|
3837
|
+
subdivisions: 4,
|
|
3838
|
+
length: 10,
|
|
3839
|
+
width: 1,
|
|
3840
|
+
color: "#57534e"
|
|
3841
|
+
}
|
|
3842
|
+
},
|
|
3843
|
+
labels: {
|
|
3844
|
+
visible: true,
|
|
3845
|
+
fontSize: 11,
|
|
3846
|
+
fontWeight: 700,
|
|
3847
|
+
rotation: "horizontal"
|
|
3848
|
+
},
|
|
3849
|
+
pointer: {
|
|
3850
|
+
visible: false
|
|
3851
|
+
},
|
|
3852
|
+
indicator: {
|
|
3853
|
+
visible: true,
|
|
3854
|
+
type: "hairline",
|
|
3855
|
+
position: "cross",
|
|
3856
|
+
length: 84,
|
|
3857
|
+
centerOffset: 0,
|
|
3858
|
+
width: 2.4,
|
|
3859
|
+
color: "#991b1b",
|
|
3860
|
+
strokeColor: "#fffef8",
|
|
3861
|
+
strokeWidth: 0.8,
|
|
3862
|
+
cap: {
|
|
3863
|
+
visible: false,
|
|
3864
|
+
stackOrder: "above",
|
|
3865
|
+
radius: 4.5,
|
|
3866
|
+
color: "#991b1b",
|
|
3867
|
+
strokeColor: "#fffef8",
|
|
3868
|
+
strokeWidth: 1.2
|
|
3869
|
+
}
|
|
3870
|
+
},
|
|
3871
|
+
light: {
|
|
3872
|
+
visible: false,
|
|
3873
|
+
x: 72,
|
|
3874
|
+
y: 60,
|
|
3875
|
+
radius: 8,
|
|
3876
|
+
color: "amber"
|
|
3877
|
+
},
|
|
3878
|
+
readout: {
|
|
3879
|
+
title: {
|
|
3880
|
+
visible: true,
|
|
3881
|
+
text: "LINEAR",
|
|
3882
|
+
x: "auto",
|
|
3883
|
+
y: 60,
|
|
3884
|
+
fontSize: 13,
|
|
3885
|
+
letterSpacing: 1.6
|
|
3886
|
+
},
|
|
3887
|
+
value: {
|
|
3888
|
+
visible: false,
|
|
3889
|
+
x: "auto",
|
|
3890
|
+
y: -42,
|
|
3891
|
+
fontSize: 24,
|
|
3892
|
+
fontWeight: 750
|
|
3893
|
+
},
|
|
3894
|
+
unit: {
|
|
3895
|
+
visible: false,
|
|
3896
|
+
x: "auto",
|
|
3897
|
+
y: -60,
|
|
3898
|
+
fontSize: 10
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
};
|
|
3902
|
+
var classicLinearLayers = [
|
|
3903
|
+
faceLayer,
|
|
3904
|
+
linearZonesLayer,
|
|
3905
|
+
linearTicksLayer,
|
|
3906
|
+
linearLabelsLayer,
|
|
3907
|
+
lightLayer,
|
|
3908
|
+
readoutLayer,
|
|
3909
|
+
linearIndicatorLayer
|
|
3910
|
+
];
|
|
3911
|
+
var classicLinearGaugeType = {
|
|
3912
|
+
description: "A framed vintage linear instrument with zones, ticks, labels, and a moving hairline.",
|
|
3913
|
+
defaults: classicLinearDefaults,
|
|
3914
|
+
configure: configureClassicLinearSize,
|
|
3915
|
+
layers: classicLinearLayers
|
|
3916
|
+
};
|
|
3917
|
+
|
|
3918
|
+
// src/layers/zero-marker.js
|
|
3919
|
+
var zeroMarkerLayer = {
|
|
3920
|
+
id: "zero-marker",
|
|
3921
|
+
render(context) {
|
|
3922
|
+
var _a;
|
|
3923
|
+
const { options, renderer } = context;
|
|
3924
|
+
const marker = (_a = options.centerZero) == null ? void 0 : _a.marker;
|
|
3925
|
+
if (!(marker == null ? void 0 : marker.visible)) return null;
|
|
3926
|
+
const group = renderer.group("zero-marker", { "aria-hidden": "true" });
|
|
3927
|
+
const line = options.geometry.mode === "linear" ? linearZeroLine(options, marker) : radialZeroLine(options, marker);
|
|
3928
|
+
group.append(svgElement("line", {
|
|
3929
|
+
class: "gaugeit__zero-marker",
|
|
3930
|
+
...line,
|
|
3931
|
+
stroke: marker.color,
|
|
3932
|
+
"stroke-width": marker.width,
|
|
3933
|
+
"stroke-linecap": marker.lineCap,
|
|
3934
|
+
opacity: marker.opacity
|
|
3935
|
+
}));
|
|
3936
|
+
return null;
|
|
3937
|
+
}
|
|
3938
|
+
};
|
|
3939
|
+
function radialZeroLine(options, marker) {
|
|
3940
|
+
var _a;
|
|
3941
|
+
const angle = valueToAngle(options.centerZero.zeroValue, options);
|
|
3942
|
+
const radius = (_a = marker.radius) != null ? _a : options.scale.radius;
|
|
3943
|
+
const start = polarPoint(
|
|
3944
|
+
options.geometry.centerX,
|
|
3945
|
+
options.geometry.centerY,
|
|
3946
|
+
Math.max(0, radius - marker.length),
|
|
3947
|
+
angle
|
|
3948
|
+
);
|
|
3949
|
+
const end = polarPoint(options.geometry.centerX, options.geometry.centerY, radius, angle);
|
|
3950
|
+
return { x1: start.x, y1: start.y, x2: end.x, y2: end.y };
|
|
3951
|
+
}
|
|
3952
|
+
function linearZeroLine(options, marker) {
|
|
3953
|
+
const axis = linearAxis(options);
|
|
3954
|
+
const point = valueToLinearPoint(options.centerZero.zeroValue, options);
|
|
3955
|
+
const span = linearNormalSpan(
|
|
3956
|
+
marker.length,
|
|
3957
|
+
marker.position,
|
|
3958
|
+
linearNormalOffset(marker.centerOffset)
|
|
3959
|
+
);
|
|
3960
|
+
const start = offsetLinearPoint(point, axis, span.start);
|
|
3961
|
+
const end = offsetLinearPoint(point, axis, span.end);
|
|
3962
|
+
return { x1: start.x, y1: start.y, x2: end.x, y2: end.y };
|
|
3963
|
+
}
|
|
3964
|
+
|
|
3965
|
+
// src/types/classic-linear-zero.js
|
|
3966
|
+
var defaults = deepMerge(classicLinearDefaults, {
|
|
3967
|
+
min: -100,
|
|
3968
|
+
max: 100,
|
|
3969
|
+
value: 0,
|
|
3970
|
+
centerZero: {
|
|
3971
|
+
zeroValue: 0,
|
|
3972
|
+
symmetric: true,
|
|
3973
|
+
marker: {
|
|
3974
|
+
visible: true,
|
|
3975
|
+
position: "cross",
|
|
3976
|
+
centerOffset: 0,
|
|
3977
|
+
length: 34,
|
|
3978
|
+
width: 3.2,
|
|
3979
|
+
color: "#111827",
|
|
3980
|
+
opacity: 1,
|
|
3981
|
+
lineCap: "butt"
|
|
3982
|
+
}
|
|
3983
|
+
},
|
|
3984
|
+
zones: [
|
|
3985
|
+
{ min: -100, max: -35, color: "#b91c1c", width: 7 },
|
|
3986
|
+
{ min: -35, max: 35, color: "transparent" },
|
|
3987
|
+
{ min: 35, max: 100, color: "#15803d", width: 7 }
|
|
3988
|
+
],
|
|
3989
|
+
readout: {
|
|
3990
|
+
title: {
|
|
3991
|
+
visible: true,
|
|
3992
|
+
text: "DEVIATION"
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
});
|
|
3996
|
+
var classicLinearZeroGaugeType = {
|
|
3997
|
+
description: "A classic linear instrument with a fixed center-zero reference and signed travel.",
|
|
3998
|
+
defaults,
|
|
3999
|
+
configure: configureClassicLinearSize,
|
|
4000
|
+
layers: [
|
|
4001
|
+
...classicLinearLayers.slice(0, 3),
|
|
4002
|
+
zeroMarkerLayer,
|
|
4003
|
+
...classicLinearLayers.slice(3)
|
|
4004
|
+
]
|
|
4005
|
+
};
|
|
4006
|
+
|
|
4007
|
+
// src/types/center-zero.js
|
|
4008
|
+
var centerZeroGaugeType = {
|
|
4009
|
+
description: "A heritage-styled bidirectional center-zero dial for deviation, balance, and signed values.",
|
|
4010
|
+
defaults: {
|
|
4011
|
+
min: -100,
|
|
4012
|
+
max: 100,
|
|
4013
|
+
value: 0,
|
|
4014
|
+
geometry: {
|
|
4015
|
+
width: 320,
|
|
4016
|
+
height: 320,
|
|
4017
|
+
centerX: 160,
|
|
4018
|
+
centerY: 160,
|
|
4019
|
+
radius: 132,
|
|
4020
|
+
startAngle: 150,
|
|
4021
|
+
endAngle: 390
|
|
4022
|
+
},
|
|
4023
|
+
centerZero: {
|
|
4024
|
+
zeroValue: 0,
|
|
4025
|
+
symmetric: true,
|
|
4026
|
+
marker: {
|
|
4027
|
+
visible: true,
|
|
4028
|
+
radius: 124,
|
|
4029
|
+
length: 25,
|
|
4030
|
+
width: 3.2,
|
|
4031
|
+
color: "#292524",
|
|
4032
|
+
opacity: 1,
|
|
4033
|
+
lineCap: "round"
|
|
4034
|
+
}
|
|
4035
|
+
},
|
|
4036
|
+
face: {
|
|
4037
|
+
visible: true,
|
|
4038
|
+
shape: "circle",
|
|
4039
|
+
fit: "geometry",
|
|
4040
|
+
clipContent: true,
|
|
4041
|
+
x: 8,
|
|
4042
|
+
y: 8,
|
|
4043
|
+
width: 304,
|
|
4044
|
+
height: 304,
|
|
4045
|
+
color: "#fffdf5",
|
|
4046
|
+
strokeColor: "#374151",
|
|
4047
|
+
strokeWidth: 4,
|
|
4048
|
+
innerStrokeColor: "#c7bfae",
|
|
4049
|
+
innerStrokeWidth: 1.4,
|
|
4050
|
+
shadow: true,
|
|
4051
|
+
glass: true
|
|
4052
|
+
},
|
|
4053
|
+
track: {
|
|
4054
|
+
visible: true,
|
|
4055
|
+
color: "#d6d0c2",
|
|
4056
|
+
width: 10,
|
|
4057
|
+
radius: 123,
|
|
4058
|
+
opacity: 1,
|
|
4059
|
+
lineCap: "round",
|
|
4060
|
+
showUnderZones: true
|
|
4061
|
+
},
|
|
4062
|
+
zones: [
|
|
4063
|
+
{ min: -100, max: -35, color: "#b91c1c", width: 8, radius: 123, opacity: 0.92, lineCap: "round" },
|
|
4064
|
+
{ min: -35, max: 35, color: "transparent" },
|
|
4065
|
+
{ min: 35, max: 100, color: "#b45309", width: 8, radius: 123, opacity: 0.92, lineCap: "round" }
|
|
4066
|
+
],
|
|
4067
|
+
scale: {
|
|
4068
|
+
radius: 119,
|
|
4069
|
+
position: "inside",
|
|
4070
|
+
major: {
|
|
4071
|
+
visible: true,
|
|
4072
|
+
interval: 25,
|
|
4073
|
+
divisions: 8,
|
|
4074
|
+
length: 18,
|
|
4075
|
+
width: 2.4,
|
|
4076
|
+
color: "#292524"
|
|
4077
|
+
},
|
|
4078
|
+
minor: {
|
|
4079
|
+
visible: true,
|
|
4080
|
+
subdivisions: 4,
|
|
4081
|
+
length: 9,
|
|
4082
|
+
width: 1,
|
|
4083
|
+
color: "#78716c"
|
|
4084
|
+
}
|
|
4085
|
+
},
|
|
4086
|
+
labels: {
|
|
4087
|
+
visible: true,
|
|
4088
|
+
interval: 25,
|
|
4089
|
+
radius: 88,
|
|
4090
|
+
color: "#292524",
|
|
4091
|
+
fontSize: 11,
|
|
4092
|
+
fontWeight: 700
|
|
4093
|
+
},
|
|
4094
|
+
pointer: {
|
|
4095
|
+
type: "spear",
|
|
4096
|
+
length: 101,
|
|
4097
|
+
shaftLength: 78,
|
|
4098
|
+
tailLength: 18,
|
|
4099
|
+
width: 8,
|
|
4100
|
+
color: "#7f1d1d",
|
|
4101
|
+
strokeColor: "#fffdf5",
|
|
4102
|
+
strokeWidth: 0.8,
|
|
4103
|
+
cap: {
|
|
4104
|
+
visible: true,
|
|
4105
|
+
stackOrder: "above",
|
|
4106
|
+
radius: 10,
|
|
4107
|
+
color: "#292524",
|
|
4108
|
+
strokeColor: "#d6d3d1",
|
|
4109
|
+
strokeWidth: 2
|
|
4110
|
+
}
|
|
4111
|
+
},
|
|
4112
|
+
light: {
|
|
4113
|
+
visible: false,
|
|
4114
|
+
x: 112,
|
|
4115
|
+
y: 205,
|
|
4116
|
+
radius: 8,
|
|
4117
|
+
color: "amber"
|
|
4118
|
+
},
|
|
4119
|
+
readout: {
|
|
4120
|
+
title: {
|
|
4121
|
+
visible: true,
|
|
4122
|
+
text: "BALANCE",
|
|
4123
|
+
x: "auto",
|
|
4124
|
+
y: 205,
|
|
4125
|
+
color: "#44403c",
|
|
4126
|
+
fontSize: 13,
|
|
4127
|
+
letterSpacing: 1.8
|
|
4128
|
+
},
|
|
4129
|
+
value: {
|
|
4130
|
+
visible: true,
|
|
4131
|
+
x: "auto",
|
|
4132
|
+
y: 238,
|
|
4133
|
+
color: "#292524",
|
|
4134
|
+
fontSize: 28,
|
|
4135
|
+
fontWeight: 760,
|
|
4136
|
+
prefix: ""
|
|
4137
|
+
},
|
|
4138
|
+
unit: {
|
|
4139
|
+
visible: false,
|
|
4140
|
+
text: "",
|
|
4141
|
+
x: "auto",
|
|
4142
|
+
y: 258,
|
|
4143
|
+
color: "#78716c",
|
|
4144
|
+
fontSize: 10
|
|
4145
|
+
}
|
|
4146
|
+
}
|
|
4147
|
+
},
|
|
4148
|
+
layers: [faceLayer, zonesLayer, ticksLayer, labelsLayer, zeroMarkerLayer, lightLayer, readoutLayer, pointerLayer]
|
|
4149
|
+
};
|
|
4150
|
+
|
|
4151
|
+
// src/layers/rolling-tape.js
|
|
4152
|
+
var rollingTapeLayer = {
|
|
4153
|
+
id: "rolling-tape",
|
|
4154
|
+
render(context) {
|
|
4155
|
+
const { options, renderer } = context;
|
|
4156
|
+
const tape = options.tape;
|
|
4157
|
+
const windowBox = tapeWindowBox(options);
|
|
4158
|
+
const vertical = tape.orientation !== "horizontal";
|
|
4159
|
+
const group = renderer.group("rolling-tape", { "aria-hidden": "true" });
|
|
4160
|
+
const clipId = `${renderer.id}-rolling-tape-clip`;
|
|
4161
|
+
const fadeStartId = `${renderer.id}-rolling-tape-fade-start`;
|
|
4162
|
+
const fadeEndId = `${renderer.id}-rolling-tape-fade-end`;
|
|
4163
|
+
const defs = svgElement("defs");
|
|
4164
|
+
const clip = svgElement("clipPath", { id: clipId, clipPathUnits: "userSpaceOnUse" });
|
|
4165
|
+
clip.append(svgElement("rect", {
|
|
4166
|
+
x: windowBox.x,
|
|
4167
|
+
y: windowBox.y,
|
|
4168
|
+
width: windowBox.width,
|
|
4169
|
+
height: windowBox.height,
|
|
4170
|
+
rx: tape.cornerRadius,
|
|
4171
|
+
ry: tape.cornerRadius
|
|
4172
|
+
}));
|
|
4173
|
+
defs.append(clip);
|
|
4174
|
+
defs.append(fadeGradient(fadeStartId, vertical, true, tape.fadeColor));
|
|
4175
|
+
defs.append(fadeGradient(fadeEndId, vertical, false, tape.fadeColor));
|
|
4176
|
+
group.append(defs);
|
|
4177
|
+
group.append(svgElement("rect", {
|
|
4178
|
+
class: "gaugeit__tape-window-background",
|
|
4179
|
+
x: windowBox.x,
|
|
4180
|
+
y: windowBox.y,
|
|
4181
|
+
width: windowBox.width,
|
|
4182
|
+
height: windowBox.height,
|
|
4183
|
+
rx: tape.cornerRadius,
|
|
4184
|
+
ry: tape.cornerRadius,
|
|
4185
|
+
fill: tape.windowColor,
|
|
4186
|
+
stroke: "none"
|
|
4187
|
+
}));
|
|
4188
|
+
const strip = svgElement("g", {
|
|
4189
|
+
class: "gaugeit__tape-strip",
|
|
4190
|
+
"clip-path": `url(#${clipId})`
|
|
4191
|
+
});
|
|
4192
|
+
strip.append(svgElement("rect", {
|
|
4193
|
+
x: windowBox.x,
|
|
4194
|
+
y: windowBox.y,
|
|
4195
|
+
width: windowBox.width,
|
|
4196
|
+
height: windowBox.height,
|
|
4197
|
+
fill: tape.stripColor
|
|
4198
|
+
}));
|
|
4199
|
+
group.append(strip);
|
|
4200
|
+
const minorStep = tapeMinorStep(options);
|
|
4201
|
+
const minorSpacing = tapeMinorSpacing(options);
|
|
4202
|
+
const majorEvery = Math.max(1, Math.floor(resolveTapeInterval(options) / minorStep + 0.5));
|
|
4203
|
+
const visibleLength = vertical ? windowBox.height : windowBox.width;
|
|
4204
|
+
const slotCount = Math.max(9, Math.ceil(visibleLength / minorSpacing) + 8);
|
|
4205
|
+
const halfSlots = Math.ceil(slotCount / 2);
|
|
4206
|
+
const slots = [];
|
|
4207
|
+
for (let index = 0; index < slotCount; index += 1) {
|
|
4208
|
+
const slot = createSlot(vertical, options);
|
|
4209
|
+
strip.append(slot.group);
|
|
4210
|
+
slots.push(slot);
|
|
4211
|
+
}
|
|
4212
|
+
drawFades(group, windowBox, tape, fadeStartId, fadeEndId, vertical);
|
|
4213
|
+
drawFixedIndicator(group, windowBox, tape, vertical);
|
|
4214
|
+
drawWindowBorder(group, windowBox, tape);
|
|
4215
|
+
return {
|
|
4216
|
+
update(value, state = {}) {
|
|
4217
|
+
const current = Number.isFinite(state.pointerValue) ? state.pointerValue : value;
|
|
4218
|
+
const centerIndex = Math.floor((current - options.min) / minorStep);
|
|
4219
|
+
for (let slotIndex = 0; slotIndex < slots.length; slotIndex += 1) {
|
|
4220
|
+
const tapeIndex = centerIndex + slotIndex - halfSlots;
|
|
4221
|
+
const slotValue = options.min + tapeIndex * minorStep;
|
|
4222
|
+
const slot = slots[slotIndex];
|
|
4223
|
+
if (slotValue < options.min - minorStep * 0.25 || slotValue > options.max + minorStep * 0.25) {
|
|
4224
|
+
slot.group.setAttribute("display", "none");
|
|
4225
|
+
continue;
|
|
4226
|
+
}
|
|
4227
|
+
const offset = tapeValueOffset(slotValue, current, options);
|
|
4228
|
+
const coordinate = vertical ? { x: windowBox.x + windowBox.width / 2, y: windowBox.y + windowBox.height / 2 - offset } : { x: windowBox.x + windowBox.width / 2 + offset, y: windowBox.y + windowBox.height / 2 };
|
|
4229
|
+
slot.group.removeAttribute("display");
|
|
4230
|
+
slot.group.setAttribute("transform", `translate(${coordinate.x} ${coordinate.y})`);
|
|
4231
|
+
const isMajor = positiveModulo2(tapeIndex, majorEvery) === 0;
|
|
4232
|
+
updateSlot(slot, slotValue, isMajor, options);
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
};
|
|
4236
|
+
}
|
|
4237
|
+
};
|
|
4238
|
+
function createSlot(vertical, options) {
|
|
4239
|
+
const group = svgElement("g", { class: "gaugeit__tape-slot" });
|
|
4240
|
+
const tick = svgElement("line", { class: "gaugeit__tape-tick" });
|
|
4241
|
+
const text = svgElement("text", {
|
|
4242
|
+
class: "gaugeit__label gaugeit__tape-label",
|
|
4243
|
+
fill: options.tape.textColor,
|
|
4244
|
+
"font-family": options.labels.fontFamily,
|
|
4245
|
+
"font-size": options.labels.fontSize,
|
|
4246
|
+
"font-weight": options.labels.fontWeight,
|
|
4247
|
+
"text-anchor": "middle",
|
|
4248
|
+
"dominant-baseline": "middle"
|
|
4249
|
+
});
|
|
4250
|
+
group.append(tick, text);
|
|
4251
|
+
group.dataset.vertical = vertical ? "true" : "false";
|
|
4252
|
+
return { group, tick, text, vertical };
|
|
4253
|
+
}
|
|
4254
|
+
function updateSlot(slot, value, major, options) {
|
|
4255
|
+
const tape = options.tape;
|
|
4256
|
+
const tickLength = major ? tape.majorTickLength : tape.minorTickLength;
|
|
4257
|
+
const zone = tape.colorByZone ? findZoneForValue(value, options.zones) : null;
|
|
4258
|
+
const zoneColor = zone && !isTransparentColor(zone.color) ? zone.color : null;
|
|
4259
|
+
const tickColor = zoneColor || (major ? tape.tickColor : tape.minorTickColor);
|
|
4260
|
+
const textColor = zoneColor || tape.textColor;
|
|
4261
|
+
const lineWidth = major ? tape.majorTickWidth : tape.minorTickWidth;
|
|
4262
|
+
if (slot.vertical) {
|
|
4263
|
+
setSvgAttributes(slot.tick, {
|
|
4264
|
+
x1: -tape.tickInset,
|
|
4265
|
+
y1: 0,
|
|
4266
|
+
x2: -tape.tickInset - tickLength,
|
|
4267
|
+
y2: 0
|
|
4268
|
+
});
|
|
4269
|
+
setSvgAttributes(slot.text, { x: tape.labelOffset, y: 0 });
|
|
4270
|
+
} else {
|
|
4271
|
+
setSvgAttributes(slot.tick, {
|
|
4272
|
+
x1: 0,
|
|
4273
|
+
y1: tape.tickInset,
|
|
4274
|
+
x2: 0,
|
|
4275
|
+
y2: tape.tickInset + tickLength
|
|
4276
|
+
});
|
|
4277
|
+
setSvgAttributes(slot.text, { x: 0, y: -tape.labelOffset });
|
|
4278
|
+
}
|
|
4279
|
+
setSvgAttributes(slot.tick, {
|
|
4280
|
+
stroke: tickColor,
|
|
4281
|
+
"stroke-width": lineWidth,
|
|
4282
|
+
"stroke-linecap": "butt",
|
|
4283
|
+
opacity: major ? 1 : tape.minorTickOpacity
|
|
4284
|
+
});
|
|
4285
|
+
slot.text.setAttribute("fill", textColor);
|
|
4286
|
+
slot.text.style.display = major && options.labels.visible ? "" : "none";
|
|
4287
|
+
slot.text.textContent = formatLabel3(value, options);
|
|
4288
|
+
}
|
|
4289
|
+
function drawFixedIndicator(group, box, tape, vertical) {
|
|
4290
|
+
const centerX = box.x + box.width / 2;
|
|
4291
|
+
const centerY = box.y + box.height / 2;
|
|
4292
|
+
const edgeInset = Math.max(0, tape.windowStrokeWidth / 2);
|
|
4293
|
+
if (vertical) {
|
|
4294
|
+
group.append(svgElement("line", {
|
|
4295
|
+
class: "gaugeit__tape-indicator",
|
|
4296
|
+
x1: box.x + edgeInset,
|
|
4297
|
+
y1: centerY,
|
|
4298
|
+
x2: box.x + box.width - edgeInset,
|
|
4299
|
+
y2: centerY,
|
|
4300
|
+
stroke: tape.indicatorColor,
|
|
4301
|
+
"stroke-width": tape.indicatorWidth,
|
|
4302
|
+
"stroke-linecap": "butt"
|
|
4303
|
+
}));
|
|
4304
|
+
} else {
|
|
4305
|
+
group.append(svgElement("line", {
|
|
4306
|
+
class: "gaugeit__tape-indicator",
|
|
4307
|
+
x1: centerX,
|
|
4308
|
+
y1: box.y + edgeInset,
|
|
4309
|
+
x2: centerX,
|
|
4310
|
+
y2: box.y + box.height - edgeInset,
|
|
4311
|
+
stroke: tape.indicatorColor,
|
|
4312
|
+
"stroke-width": tape.indicatorWidth,
|
|
4313
|
+
"stroke-linecap": "butt"
|
|
4314
|
+
}));
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
function drawWindowBorder(group, box, tape) {
|
|
4318
|
+
const outerRadius = Math.max(0, Math.min(tape.cornerRadius, box.width / 2, box.height / 2));
|
|
4319
|
+
const innerInset = Math.max(tape.windowStrokeWidth, tape.windowInnerStrokeWidth, 0);
|
|
4320
|
+
group.append(svgElement("rect", {
|
|
4321
|
+
class: "gaugeit__tape-window",
|
|
4322
|
+
x: box.x,
|
|
4323
|
+
y: box.y,
|
|
4324
|
+
width: box.width,
|
|
4325
|
+
height: box.height,
|
|
4326
|
+
rx: outerRadius,
|
|
4327
|
+
ry: outerRadius,
|
|
4328
|
+
fill: "none",
|
|
4329
|
+
stroke: tape.windowStrokeColor,
|
|
4330
|
+
"stroke-width": tape.windowStrokeWidth
|
|
4331
|
+
}));
|
|
4332
|
+
if (tape.windowInnerStrokeWidth > 0 && box.width > innerInset * 2 && box.height > innerInset * 2) {
|
|
4333
|
+
group.append(svgElement("rect", {
|
|
4334
|
+
class: "gaugeit__tape-window-inner",
|
|
4335
|
+
x: box.x + innerInset,
|
|
4336
|
+
y: box.y + innerInset,
|
|
4337
|
+
width: box.width - innerInset * 2,
|
|
4338
|
+
height: box.height - innerInset * 2,
|
|
4339
|
+
rx: Math.max(0, outerRadius - innerInset),
|
|
4340
|
+
ry: Math.max(0, outerRadius - innerInset),
|
|
4341
|
+
fill: "none",
|
|
4342
|
+
stroke: tape.windowInnerStrokeColor,
|
|
4343
|
+
"stroke-width": tape.windowInnerStrokeWidth,
|
|
4344
|
+
"pointer-events": "none"
|
|
4345
|
+
}));
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
function drawFades(group, box, tape, startId, endId, vertical) {
|
|
4349
|
+
if (tape.fadeSize <= 0) return;
|
|
4350
|
+
if (vertical) {
|
|
4351
|
+
group.append(
|
|
4352
|
+
svgElement("rect", { x: box.x, y: box.y, width: box.width, height: tape.fadeSize, fill: `url(#${startId})`, "pointer-events": "none" }),
|
|
4353
|
+
svgElement("rect", { x: box.x, y: box.y + box.height - tape.fadeSize, width: box.width, height: tape.fadeSize, fill: `url(#${endId})`, "pointer-events": "none" })
|
|
4354
|
+
);
|
|
4355
|
+
} else {
|
|
4356
|
+
group.append(
|
|
4357
|
+
svgElement("rect", { x: box.x, y: box.y, width: tape.fadeSize, height: box.height, fill: `url(#${startId})`, "pointer-events": "none" }),
|
|
4358
|
+
svgElement("rect", { x: box.x + box.width - tape.fadeSize, y: box.y, width: tape.fadeSize, height: box.height, fill: `url(#${endId})`, "pointer-events": "none" })
|
|
4359
|
+
);
|
|
4360
|
+
}
|
|
4361
|
+
}
|
|
4362
|
+
function fadeGradient(id, vertical, start, color) {
|
|
4363
|
+
const gradient = svgElement("linearGradient", {
|
|
4364
|
+
id,
|
|
4365
|
+
x1: "0%",
|
|
4366
|
+
y1: "0%",
|
|
4367
|
+
x2: vertical ? "0%" : "100%",
|
|
4368
|
+
y2: vertical ? "100%" : "0%"
|
|
4369
|
+
});
|
|
4370
|
+
const firstOpacity = start ? 0.96 : 0;
|
|
4371
|
+
const secondOpacity = start ? 0 : 0.96;
|
|
4372
|
+
gradient.append(
|
|
4373
|
+
svgElement("stop", { offset: "0%", "stop-color": color, "stop-opacity": firstOpacity }),
|
|
4374
|
+
svgElement("stop", { offset: "100%", "stop-color": color, "stop-opacity": secondOpacity })
|
|
4375
|
+
);
|
|
4376
|
+
return gradient;
|
|
4377
|
+
}
|
|
4378
|
+
function formatLabel3(value, options) {
|
|
4379
|
+
if (typeof options.labels.formatter === "function") {
|
|
4380
|
+
return String(options.labels.formatter(value, { min: options.min, max: options.max, options }));
|
|
4381
|
+
}
|
|
4382
|
+
return Number(value).toFixed(options.labels.fractionDigits);
|
|
4383
|
+
}
|
|
4384
|
+
function positiveModulo2(value, divisor) {
|
|
4385
|
+
return (value % divisor + divisor) % divisor;
|
|
4386
|
+
}
|
|
4387
|
+
|
|
4388
|
+
// src/types/rolling-tape.js
|
|
4389
|
+
var rollingTapeGaugeType = {
|
|
4390
|
+
description: "A cream-faced rolling numeric tape with a fixed edge-to-edge aircraft reference line.",
|
|
4391
|
+
defaults: {
|
|
4392
|
+
geometry: {
|
|
4393
|
+
mode: "tape",
|
|
4394
|
+
width: 320,
|
|
4395
|
+
height: 340,
|
|
4396
|
+
centerX: 160,
|
|
4397
|
+
centerY: 170
|
|
4398
|
+
},
|
|
4399
|
+
face: {
|
|
4400
|
+
visible: true,
|
|
4401
|
+
shape: "panel",
|
|
4402
|
+
fit: "geometry",
|
|
4403
|
+
clipContent: true,
|
|
4404
|
+
x: 8,
|
|
4405
|
+
y: 8,
|
|
4406
|
+
width: 304,
|
|
4407
|
+
height: 324,
|
|
4408
|
+
cornerRadius: 23,
|
|
4409
|
+
color: "#fffdf4",
|
|
4410
|
+
strokeColor: "#4b5563",
|
|
4411
|
+
strokeWidth: 4,
|
|
4412
|
+
innerStrokeColor: "#c8bfaa",
|
|
4413
|
+
innerStrokeWidth: 1.4,
|
|
4414
|
+
shadow: true,
|
|
4415
|
+
glass: true
|
|
4416
|
+
},
|
|
4417
|
+
pointer: { visible: false },
|
|
4418
|
+
indicator: { visible: false },
|
|
4419
|
+
track: { visible: false },
|
|
4420
|
+
tape: {
|
|
4421
|
+
orientation: "vertical",
|
|
4422
|
+
x: 62,
|
|
4423
|
+
y: 69,
|
|
4424
|
+
width: 196,
|
|
4425
|
+
height: 212,
|
|
4426
|
+
cornerRadius: 12,
|
|
4427
|
+
windowColor: "#fff9ea",
|
|
4428
|
+
windowStrokeColor: "#544f45",
|
|
4429
|
+
windowStrokeWidth: 2,
|
|
4430
|
+
windowInnerStrokeColor: "#d8cfbd",
|
|
4431
|
+
windowInnerStrokeWidth: 1,
|
|
4432
|
+
stripColor: "#fff9ea",
|
|
4433
|
+
textColor: "#211f1a",
|
|
4434
|
+
tickColor: "#3f3a32",
|
|
4435
|
+
minorTickColor: "#817867",
|
|
4436
|
+
minorTickOpacity: 0.86,
|
|
4437
|
+
majorTickWidth: 2.2,
|
|
4438
|
+
minorTickWidth: 1,
|
|
4439
|
+
majorTickLength: 26,
|
|
4440
|
+
minorTickLength: 13,
|
|
4441
|
+
tickInset: 66,
|
|
4442
|
+
labelOffset: 10,
|
|
4443
|
+
interval: 20,
|
|
4444
|
+
minorSubdivisions: 4,
|
|
4445
|
+
majorSpacing: 54,
|
|
4446
|
+
indicatorColor: "#991b1b",
|
|
4447
|
+
indicatorWidth: 2.8,
|
|
4448
|
+
fadeSize: 38,
|
|
4449
|
+
fadeColor: "#fffdf4",
|
|
4450
|
+
colorByZone: true
|
|
4451
|
+
},
|
|
4452
|
+
scale: {
|
|
4453
|
+
major: { visible: true, interval: 20, divisions: 5 },
|
|
4454
|
+
minor: { visible: true, subdivisions: 4 }
|
|
4455
|
+
},
|
|
4456
|
+
labels: {
|
|
4457
|
+
visible: true,
|
|
4458
|
+
color: "#211f1a",
|
|
4459
|
+
fontSize: 15,
|
|
4460
|
+
fontWeight: 760,
|
|
4461
|
+
fractionDigits: 0
|
|
4462
|
+
},
|
|
4463
|
+
zones: [
|
|
4464
|
+
{ min: 0, max: 20, color: "#b91c1c" },
|
|
4465
|
+
{ min: 20, max: 80, color: "transparent" },
|
|
4466
|
+
{ min: 80, max: 100, color: "#15803d" }
|
|
4467
|
+
],
|
|
4468
|
+
light: {
|
|
4469
|
+
visible: false,
|
|
4470
|
+
x: 72,
|
|
4471
|
+
y: 46,
|
|
4472
|
+
radius: 8,
|
|
4473
|
+
color: "amber"
|
|
4474
|
+
},
|
|
4475
|
+
readout: {
|
|
4476
|
+
title: {
|
|
4477
|
+
visible: true,
|
|
4478
|
+
text: "ALTITUDE",
|
|
4479
|
+
x: "auto",
|
|
4480
|
+
y: 42,
|
|
4481
|
+
color: "#3f3a32",
|
|
4482
|
+
fontSize: 13,
|
|
4483
|
+
letterSpacing: 1.8
|
|
4484
|
+
},
|
|
4485
|
+
value: {
|
|
4486
|
+
visible: false,
|
|
4487
|
+
x: "auto",
|
|
4488
|
+
y: 303,
|
|
4489
|
+
color: "#211f1a",
|
|
4490
|
+
fontSize: 24,
|
|
4491
|
+
fontWeight: 760
|
|
4492
|
+
},
|
|
4493
|
+
unit: {
|
|
4494
|
+
visible: true,
|
|
4495
|
+
text: "FT",
|
|
4496
|
+
x: "auto",
|
|
4497
|
+
y: 307,
|
|
4498
|
+
color: "#6b6253",
|
|
4499
|
+
fontSize: 10,
|
|
4500
|
+
letterSpacing: 1.2
|
|
4501
|
+
}
|
|
4502
|
+
}
|
|
4503
|
+
},
|
|
4504
|
+
configure: configureRollingTapeSize,
|
|
4505
|
+
layers: [faceLayer, rollingTapeLayer, lightLayer, readoutLayer]
|
|
4506
|
+
};
|
|
4507
|
+
|
|
4508
|
+
// src/types/register-builtins.js
|
|
4509
|
+
function registerBuiltInTypes(registry) {
|
|
4510
|
+
registry.register("arc", arcGaugeType).register("classic", classicGaugeType).register("classic-linear", classicLinearGaugeType).register("classic-linear-zero", classicLinearZeroGaugeType).register("center-zero", centerZeroGaugeType).register("heritage-round", heritageRoundGaugeType).register("rolling-tape", rollingTapeGaugeType).register("line-scale", lineScaleGaugeType);
|
|
4511
|
+
return registry;
|
|
4512
|
+
}
|
|
4513
|
+
var builtInTypes = Object.freeze({
|
|
4514
|
+
arc: arcGaugeType,
|
|
4515
|
+
classic: classicGaugeType,
|
|
4516
|
+
"classic-linear": classicLinearGaugeType,
|
|
4517
|
+
"classic-linear-zero": classicLinearZeroGaugeType,
|
|
4518
|
+
"center-zero": centerZeroGaugeType,
|
|
4519
|
+
"heritage-round": heritageRoundGaugeType,
|
|
4520
|
+
"rolling-tape": rollingTapeGaugeType,
|
|
4521
|
+
"line-scale": lineScaleGaugeType
|
|
4522
|
+
});
|
|
4523
|
+
|
|
4524
|
+
// src/core/auto-mount.js
|
|
4525
|
+
var mountedInstances = /* @__PURE__ */ new WeakMap();
|
|
4526
|
+
function createManagedGauge(target, options, GaugeClass, registry) {
|
|
4527
|
+
const existing = mountedInstances.get(target);
|
|
4528
|
+
if (existing) existing.destroy();
|
|
4529
|
+
const gauge = new GaugeClass(target, options, registry, managedLifecycle(target));
|
|
4530
|
+
mountedInstances.set(target, gauge);
|
|
4531
|
+
return gauge;
|
|
4532
|
+
}
|
|
4533
|
+
function autoMountElements(selector, GaugeClass, registry, root = document) {
|
|
4534
|
+
const elements = [...root.querySelectorAll(selector || "[data-gaugeit]")];
|
|
4535
|
+
return elements.map((element) => {
|
|
4536
|
+
const existing = mountedInstances.get(element);
|
|
4537
|
+
if (existing) return existing;
|
|
4538
|
+
const options = optionsFromDataset(element);
|
|
4539
|
+
const gauge = new GaugeClass(element, options, registry, managedLifecycle(element));
|
|
4540
|
+
mountedInstances.set(element, gauge);
|
|
4541
|
+
return gauge;
|
|
4542
|
+
});
|
|
4543
|
+
}
|
|
4544
|
+
function getMountedGauge(element) {
|
|
4545
|
+
return mountedInstances.get(element) || null;
|
|
4546
|
+
}
|
|
4547
|
+
function unmountGauge(element) {
|
|
4548
|
+
const gauge = mountedInstances.get(element);
|
|
4549
|
+
if (!gauge) return false;
|
|
4550
|
+
gauge.destroy();
|
|
4551
|
+
mountedInstances.delete(element);
|
|
4552
|
+
return true;
|
|
4553
|
+
}
|
|
4554
|
+
function optionsFromDataset(element) {
|
|
4555
|
+
const options = parseJsonObject(element.dataset.gaugeitOptions, {});
|
|
4556
|
+
if (element.dataset.gaugeitType) options.type = element.dataset.gaugeitType;
|
|
4557
|
+
if (element.dataset.gaugeitValue !== void 0) options.value = Number(element.dataset.gaugeitValue);
|
|
4558
|
+
if (element.dataset.gaugeitMin !== void 0) options.min = Number(element.dataset.gaugeitMin);
|
|
4559
|
+
if (element.dataset.gaugeitMax !== void 0) options.max = Number(element.dataset.gaugeitMax);
|
|
4560
|
+
return options;
|
|
4561
|
+
}
|
|
4562
|
+
function managedLifecycle(element) {
|
|
4563
|
+
return {
|
|
4564
|
+
onDestroy(gauge) {
|
|
4565
|
+
if (mountedInstances.get(element) === gauge) mountedInstances.delete(element);
|
|
4566
|
+
}
|
|
4567
|
+
};
|
|
4568
|
+
}
|
|
4569
|
+
|
|
4570
|
+
// src/web-component.js
|
|
4571
|
+
function defineCustomElement(createGauge2, tagName = "gauge-it") {
|
|
4572
|
+
if (typeof customElements === "undefined" || typeof HTMLElement === "undefined") return null;
|
|
4573
|
+
if (customElements.get(tagName)) return customElements.get(tagName);
|
|
4574
|
+
class GaugeitElement extends HTMLElement {
|
|
4575
|
+
static get observedAttributes() {
|
|
4576
|
+
return ["value", "min", "max", "type", "options"];
|
|
4577
|
+
}
|
|
4578
|
+
constructor() {
|
|
4579
|
+
super();
|
|
4580
|
+
this._gauge = null;
|
|
4581
|
+
this._mount = document.createElement("div");
|
|
4582
|
+
this._mount.className = "gaugeit-element__mount";
|
|
4583
|
+
}
|
|
4584
|
+
connectedCallback() {
|
|
4585
|
+
if (!this._mount.isConnected) this.append(this._mount);
|
|
4586
|
+
if (!this._gauge) this._gauge = createGauge2(this._mount, this._readOptions());
|
|
4587
|
+
}
|
|
4588
|
+
disconnectedCallback() {
|
|
4589
|
+
if (this._gauge) {
|
|
4590
|
+
this._gauge.destroy();
|
|
4591
|
+
this._gauge = null;
|
|
4592
|
+
}
|
|
4593
|
+
}
|
|
4594
|
+
attributeChangedCallback() {
|
|
4595
|
+
if (this._gauge) this._gauge.replaceOptions(this._readOptions());
|
|
4596
|
+
}
|
|
4597
|
+
get value() {
|
|
4598
|
+
return this._gauge ? this._gauge.getValue() : Number(this.getAttribute("value") || 0);
|
|
4599
|
+
}
|
|
4600
|
+
set value(nextValue) {
|
|
4601
|
+
this.setAttribute("value", String(nextValue));
|
|
4602
|
+
}
|
|
4603
|
+
get gauge() {
|
|
4604
|
+
return this._gauge;
|
|
4605
|
+
}
|
|
4606
|
+
_readOptions() {
|
|
4607
|
+
const options = parseJsonObject(this.getAttribute("options"), {});
|
|
4608
|
+
if (this.hasAttribute("type")) options.type = this.getAttribute("type");
|
|
4609
|
+
if (this.hasAttribute("value")) options.value = Number(this.getAttribute("value"));
|
|
4610
|
+
if (this.hasAttribute("min")) options.min = Number(this.getAttribute("min"));
|
|
4611
|
+
if (this.hasAttribute("max")) options.max = Number(this.getAttribute("max"));
|
|
4612
|
+
return options;
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4615
|
+
customElements.define(tagName, GaugeitElement);
|
|
4616
|
+
return GaugeitElement;
|
|
4617
|
+
}
|
|
4618
|
+
|
|
4619
|
+
// src/index.js
|
|
4620
|
+
registerBuiltInTypes(gaugeTypeRegistry);
|
|
4621
|
+
function createGauge(target, options = {}) {
|
|
4622
|
+
const element = typeof target === "string" ? document.querySelector(target) : target;
|
|
4623
|
+
if (!element) throw new Error(`Gaugeit target not found: ${target}`);
|
|
4624
|
+
return createManagedGauge(element, options, Gauge, gaugeTypeRegistry);
|
|
4625
|
+
}
|
|
4626
|
+
function registerGaugeType(name, definition) {
|
|
4627
|
+
gaugeTypeRegistry.register(name, definition);
|
|
4628
|
+
return Gaugeit;
|
|
4629
|
+
}
|
|
4630
|
+
function autoMount(selector = "[data-gaugeit]", root = document) {
|
|
4631
|
+
return autoMountElements(selector, Gauge, gaugeTypeRegistry, root);
|
|
4632
|
+
}
|
|
4633
|
+
function defineGaugeitElement(tagName = "gauge-it") {
|
|
4634
|
+
return defineCustomElement(createGauge, tagName);
|
|
4635
|
+
}
|
|
4636
|
+
var layers = Object.freeze({
|
|
4637
|
+
face: faceLayer,
|
|
4638
|
+
zones: zonesLayer,
|
|
4639
|
+
ticks: ticksLayer,
|
|
4640
|
+
labels: labelsLayer,
|
|
4641
|
+
pointer: pointerLayer,
|
|
4642
|
+
readout: readoutLayer,
|
|
4643
|
+
linearZones: linearZonesLayer,
|
|
4644
|
+
linearTicks: linearTicksLayer,
|
|
4645
|
+
linearLabels: linearLabelsLayer,
|
|
4646
|
+
linearIndicator: linearIndicatorLayer,
|
|
4647
|
+
zeroMarker: zeroMarkerLayer,
|
|
4648
|
+
rollingTape: rollingTapeLayer,
|
|
4649
|
+
light: lightLayer
|
|
4650
|
+
});
|
|
4651
|
+
var geometry = Object.freeze({
|
|
4652
|
+
angleToValue,
|
|
4653
|
+
arcPath,
|
|
4654
|
+
calculateGaugeBounds,
|
|
4655
|
+
buildMinorValues,
|
|
4656
|
+
buildScaleValues,
|
|
4657
|
+
centeredArcAngles,
|
|
4658
|
+
findZoneForValue,
|
|
4659
|
+
gaugeSweep,
|
|
4660
|
+
isClosedGauge,
|
|
4661
|
+
linearAxis,
|
|
4662
|
+
linearIndicatorNormalOffset,
|
|
4663
|
+
linearNormalOffset,
|
|
4664
|
+
linearNormalSpan,
|
|
4665
|
+
linearYToSvg,
|
|
4666
|
+
linearPointToValue,
|
|
4667
|
+
niceInterval,
|
|
4668
|
+
offsetLinearPoint,
|
|
4669
|
+
padBounds,
|
|
4670
|
+
polarPoint,
|
|
4671
|
+
resolveFaceBox,
|
|
4672
|
+
resolveReadoutPosition,
|
|
4673
|
+
resolveScaleEndpointValues,
|
|
4674
|
+
resolveZoneSegments,
|
|
4675
|
+
taperedArcPath,
|
|
4676
|
+
unionBounds,
|
|
4677
|
+
valueToAngle,
|
|
4678
|
+
valueToLinearPoint,
|
|
4679
|
+
valueToLinearRatio,
|
|
4680
|
+
resolveTapeInterval,
|
|
4681
|
+
tapeMinorSpacing,
|
|
4682
|
+
tapeMinorStep,
|
|
4683
|
+
tapeValueOffset,
|
|
4684
|
+
tapeWindowBox,
|
|
4685
|
+
lightIsActive,
|
|
4686
|
+
resolveLightPosition
|
|
4687
|
+
});
|
|
4688
|
+
var types = builtInTypes;
|
|
4689
|
+
var VERSION = "0.1.0";
|
|
4690
|
+
var Gaugeit = Object.freeze({
|
|
4691
|
+
VERSION,
|
|
4692
|
+
Gauge,
|
|
4693
|
+
GaugeTypeRegistry,
|
|
4694
|
+
createGauge,
|
|
4695
|
+
registerGaugeType,
|
|
4696
|
+
autoMount,
|
|
4697
|
+
defineGaugeitElement,
|
|
4698
|
+
getMountedGauge,
|
|
4699
|
+
unmountGauge,
|
|
4700
|
+
registry: gaugeTypeRegistry,
|
|
4701
|
+
types,
|
|
4702
|
+
layers,
|
|
4703
|
+
geometry,
|
|
4704
|
+
deepMerge,
|
|
4705
|
+
easingNames
|
|
4706
|
+
});
|
|
4707
|
+
var index_default = Gaugeit;
|
|
4708
|
+
|
|
4709
|
+
// src/standalone.js
|
|
4710
|
+
var STYLE_ID = "gaugeit-styles";
|
|
4711
|
+
if (typeof document !== "undefined" && !document.getElementById(STYLE_ID)) {
|
|
4712
|
+
const style = document.createElement("style");
|
|
4713
|
+
style.id = STYLE_ID;
|
|
4714
|
+
style.dataset.gaugeitStyles = "standalone";
|
|
4715
|
+
style.textContent = gaugeit_default;
|
|
4716
|
+
(document.head || document.documentElement).append(style);
|
|
4717
|
+
}
|
|
4718
|
+
var standalone_default = index_default;
|
|
4719
|
+
return __toCommonJS(standalone_exports);
|
|
4720
|
+
})();
|