Files
HomeAssistantVS/www/community/sensor-bar-card-plus/sensor-bar-card-plus.js
T

12726 lines
656 KiB
JavaScript

(() => {
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res, err) => function __init() {
if (err) throw err[0];
try {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
} catch (e) {
throw err = [e], e;
}
};
var __commonJS = (cb, mod) => function __require() {
try {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
} catch (e) {
throw mod = 0, e;
}
};
// src/utils/extrema.js
function getLocalBoundaryTimestamp(date, unit) {
if (!(date instanceof Date) || Number.isNaN(date.getTime())) return null;
const year = date.getFullYear();
const month = date.getMonth();
const day = date.getDate();
const hour = date.getHours();
if (unit === "quarterly") {
return new Date(year, month, day, hour, Math.floor(date.getMinutes() / 15) * 15, 0, 0).getTime();
}
if (unit === "hourly") {
return new Date(year, month, day, hour, 0, 0, 0).getTime();
}
if (unit === "daily") {
return new Date(year, month, day, 0, 0, 0, 0).getTime();
}
if (unit === "weekly") {
const daysSinceMonday = (date.getDay() + 6) % 7;
return new Date(year, month, day - daysSinceMonday, 0, 0, 0, 0).getTime();
}
if (unit === "monthly") {
return new Date(year, month, 1, 0, 0, 0, 0).getTime();
}
if (unit === "yearly") {
return new Date(year, 0, 1, 0, 0, 0, 0).getTime();
}
return null;
}
function normalizeReset(value) {
if (value === void 0) value = "never";
if (typeof value !== "string") return { kind: "never" };
const normalized = value.trim().toLowerCase();
if (normalized === "never") return { kind: "never" };
if (CALENDAR_RESETS.has(normalized)) {
return { kind: "calendar", unit: normalized };
}
const durationMatch = normalized.match(/^(\d+)(m|h)$/);
if (!durationMatch) return { kind: "never" };
const amount = Number(durationMatch[1]);
const unit = durationMatch[2];
if (!Number.isInteger(amount) || amount < 1 || unit === "m" && amount > 59 || unit === "h" && amount > 23) {
return { kind: "never" };
}
return unit === "m" ? { kind: "duration", minutes: amount } : { kind: "duration", hours: amount };
}
function isValidReset(value) {
if (typeof value !== "string") return false;
const normalized = value.trim().toLowerCase();
if (normalized === "never" || CALENDAR_RESETS.has(normalized)) return true;
const match = normalized.match(/^(\d+)(m|h)$/);
if (!match) return false;
const amount = Number(match[1]);
return Number.isInteger(amount) && amount >= 1 && (match[2] === "m" ? amount <= 59 : amount <= 23);
}
function getResetWindowKey(reset, timestamp) {
if ((reset == null ? void 0 : reset.kind) !== "calendar") return null;
const boundary = getLocalBoundaryTimestamp(new Date(timestamp), reset.unit);
return Number.isFinite(boundary) ? String(boundary) : null;
}
function getDurationMs(reset) {
if ((reset == null ? void 0 : reset.kind) !== "duration") return null;
if (Number.isInteger(reset.minutes)) return reset.minutes * 60 * 1e3;
if (Number.isInteger(reset.hours)) return reset.hours * 60 * 60 * 1e3;
return null;
}
function initializeExtremum(sample, reset, timestamp) {
if ((reset == null ? void 0 : reset.kind) === "duration") {
return {
value: sample,
startedAtMs: Number.isFinite(timestamp) ? timestamp : null,
windowKey: null
};
}
if ((reset == null ? void 0 : reset.kind) === "calendar") {
return {
value: sample,
startedAtMs: null,
windowKey: getResetWindowKey(reset, timestamp)
};
}
return {
value: sample,
startedAtMs: null,
windowKey: null
};
}
function updateExtremum(previous, sample, reset, direction, timestamp) {
if (!Number.isFinite(sample)) return previous != null ? previous : null;
const durationMs = getDurationMs(reset);
if (!previous) return initializeExtremum(sample, reset, timestamp);
if (durationMs !== null && Number.isFinite(previous.startedAtMs) && Number.isFinite(timestamp) && timestamp - previous.startedAtMs >= durationMs) {
return initializeExtremum(sample, reset, timestamp);
}
if ((reset == null ? void 0 : reset.kind) === "calendar") {
const windowKey = getResetWindowKey(reset, timestamp);
if (windowKey !== null && windowKey !== previous.windowKey) {
return initializeExtremum(sample, reset, timestamp);
}
}
const shouldReplace = direction === "min" ? sample < previous.value : sample > previous.value;
return shouldReplace ? { ...previous, value: sample } : previous;
}
var CALENDAR_RESETS;
var init_extrema = __esm({
"src/utils/extrema.js"() {
CALENDAR_RESETS = /* @__PURE__ */ new Set([
"quarterly",
"hourly",
"daily",
"weekly",
"monthly",
"yearly"
]);
}
});
// src/config/normalize.js
function normalizeResolvableValue(value, entityValue, percentValue = null) {
const normalized = {
fixed: value != null ? value : null,
entity: entityValue != null ? entityValue : null
};
if (Number.isFinite(percentValue)) {
normalized.percent = percentValue;
}
return normalized;
}
function looksLikeEntityId(value) {
return typeof value === "string" && /^[a-z0-9_]+\.[a-z0-9_]+$/i.test(value.trim());
}
function parsePercentLiteral(value) {
if (typeof value !== "string") return null;
const match = value.match(/^\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*%\s*$/);
if (!match) return null;
const percent = parseFloat(match[1]);
return Number.isFinite(percent) ? percent : null;
}
function getFiniteNumber(value) {
if (typeof value === "number") {
return Number.isFinite(value) ? value : null;
}
if (typeof value === "string") {
const trimmed = value.trim();
if (!trimmed) return null;
const num = Number(trimmed);
return Number.isFinite(num) ? num : null;
}
return null;
}
function normalizeStructuredResolvableValue(input, inheritedResolvable = null, defaultValue = null, options = {}) {
var _a, _b, _c, _d, _e, _f;
const { allowPercent = false } = options;
const inherited = inheritedResolvable != null ? inheritedResolvable : normalizeResolvableValue(defaultValue, null);
if (input === void 0) {
return { ...inherited };
}
if (input === null) {
return normalizeResolvableValue(null, null);
}
if (typeof input === "object" && !Array.isArray(input)) {
const value = (_b = (_a = input.fixed) != null ? _a : input.value) != null ? _b : null;
const entity = (_c = input.entity) != null ? _c : null;
const percent = allowPercent ? getFiniteNumber(input.percent) : null;
return normalizeResolvableValue(value, entity, percent);
}
if (looksLikeEntityId(input)) {
return normalizeResolvableValue(
(_e = (_d = inherited.fixed) != null ? _d : defaultValue) != null ? _e : null,
input,
(_f = inherited.percent) != null ? _f : null
);
}
if (allowPercent) {
const percent = parsePercentLiteral(input);
if (Number.isFinite(percent)) {
return normalizeResolvableValue(null, null, percent);
}
}
return normalizeResolvableValue(input, null);
}
function hasConfiguredMarkerSource(source) {
var _a;
return !!source && (getFiniteNumber((_a = source.fixed) != null ? _a : source.value) !== null || Number.isFinite(source.percent) || !!source.entity);
}
function applyGenericMarkerCapacity(markers, config) {
var _a, _b, _c, _d;
const occupied = {
above: ((_a = config == null ? void 0 : config.peak_marker) == null ? void 0 : _a.show) === true ? 1 : 0,
below: (((_b = config == null ? void 0 : config.floor_marker) == null ? void 0 : _b.show) === true ? 1 : 0) + (((_c = config == null ? void 0 : config.target_marker) == null ? void 0 : _c.enabled) !== false && hasConfiguredMarkerSource((_d = config == null ? void 0 : config.target_marker) == null ? void 0 : _d.source) ? 1 : 0)
};
return markers.map((marker) => {
const accepted = marker.valid && occupied[marker.lane] < 4;
if (accepted) occupied[marker.lane] += 1;
return { ...marker, accepted };
});
}
function normalizeGenericMarkerList(input, capacityConfig = null) {
if (!Array.isArray(input)) {
return { markers: [], invalidList: input !== void 0 };
}
const markers = input.map((rawMarker, index) => {
var _a;
if (!rawMarker || typeof rawMarker !== "object" || Array.isArray(rawMarker)) {
return { id: `generic-${index}`, index, valid: false, accepted: false, malformed: true };
}
const rawAt = rawMarker.at;
const hasUnsupportedPercentField = rawAt && typeof rawAt === "object" && Object.prototype.hasOwnProperty.call(rawAt, "percent");
const atInput = typeof rawAt === "string" ? rawAt.trim() : rawAt;
const source = normalizeStructuredResolvableValue(atInput, null, null, { allowPercent: true });
const explicitEntity = rawAt && typeof rawAt === "object" && rawAt.entity !== void 0 ? rawAt.entity : typeof atInput === "string" && looksLikeEntityId(atInput) ? atInput : null;
const invalidEntity = explicitEntity !== null && explicitEntity !== void 0 && explicitEntity !== "" && !looksLikeEntityId(explicitEntity);
const invalidFixed = rawAt && typeof rawAt === "object" && rawAt.fixed !== void 0 && rawAt.fixed !== null && getFiniteNumber(rawAt.fixed) === null;
const hasSource = !!source.entity || getFiniteNumber(source.fixed) !== null || Number.isFinite(source.percent);
const invalidPercentage = Number.isFinite(source.percent) && (source.percent < 0 || source.percent > 100);
const validSource = rawAt !== void 0 && rawAt !== null && !hasUnsupportedPercentField && !invalidEntity && !invalidFixed && hasSource && !invalidPercentage;
const lane = rawMarker.lane === void 0 ? "below" : rawMarker.lane;
const validLane = lane === "above" || lane === "below";
const supportedShapes = ["circle", "diamond", "triangle", "chevron", "arrow", "pin"];
const validShape = rawMarker.shape === void 0 || supportedShapes.includes(rawMarker.shape);
const direction = normalizeMarkerDirection(rawMarker.direction);
const invalidDirection = rawMarker.direction !== void 0 && !["inward", "outward"].includes(
typeof rawMarker.direction === "string" ? rawMarker.direction.trim().toLowerCase() : ""
);
const label = rawMarker.label && typeof rawMarker.label === "object" && !Array.isArray(rawMarker.label) ? rawMarker.label : {};
const labelConfig = normalizeMarkerLabelConfig(label);
const labelEntityInput = label.entity;
const labelEntity = typeof labelEntityInput === "string" ? labelEntityInput.trim() : "";
const invalidLabelEntity = labelEntityInput !== void 0 && labelEntityInput !== null && labelEntityInput !== "" && !looksLikeEntityId(labelEntity);
const validLabelEntity = labelEntity && looksLikeEntityId(labelEntity) ? labelEntity : null;
const showMarker = typeof rawMarker.show_marker === "boolean" ? rawMarker.show_marker : true;
return {
id: `generic-${index}`,
index,
source: {
...source,
entity: typeof source.entity === "string" ? source.entity.trim() : source.entity
},
lane: validLane ? lane : null,
shape: validShape ? (_a = rawMarker.shape) != null ? _a : "circle" : "circle",
direction,
invalidDirection,
showMarker,
invalidShowMarker: rawMarker.show_marker !== void 0 && typeof rawMarker.show_marker !== "boolean",
color: typeof rawMarker.color === "string" && rawMarker.color.trim() ? rawMarker.color : "#888888",
label: {
show: label.show === true,
text: labelConfig.label_text,
entity: validLabelEntity,
showValue: labelConfig.label_show_value,
showUnit: labelConfig.label_show_unit,
precision: labelConfig.label_precision,
invalidShow: labelConfig.label_invalid_show,
invalidText: labelConfig.label_invalid_text,
invalidShowValue: labelConfig.label_invalid_show_value,
invalidShowUnit: labelConfig.label_invalid_show_unit,
invalidPrecision: labelConfig.label_invalid_precision,
invalidPrecisionKey: labelConfig.label_precision_key,
unsupportedUnit: labelConfig.label_unsupported_unit,
invalidEntity: invalidLabelEntity
},
valid: validSource && validLane,
invalidSource: !validSource,
invalidPercentage,
unsupportedPercentField: hasUnsupportedPercentField,
invalidLane: !validLane,
invalidShape: !validShape,
accepted: false
};
});
return { markers: applyGenericMarkerCapacity(markers, capacityConfig), invalidList: false };
}
function normalizeBaselineDirectionConfig(input, inheritedDirection = null) {
var _a;
const inherited = inheritedDirection != null ? inheritedDirection : { color: null };
if (input === void 0) {
return { ...inherited };
}
if (input === null) {
return { color: null };
}
if (typeof input === "object" && !Array.isArray(input)) {
return {
color: (_a = input.color) != null ? _a : null
};
}
return {
color: input
};
}
function normalizeOptionalEnabled(value) {
return value === true ? true : value === false ? false : null;
}
function normalizeBaselineConfig(entityConfig, cardConfig) {
var _a;
const cardBaseline = cardConfig == null ? void 0 : cardConfig.baseline;
const rawBaseline = entityConfig == null ? void 0 : entityConfig.baseline;
const inherited = {
enabled: normalizeOptionalEnabled(cardBaseline == null ? void 0 : cardBaseline.enabled),
at: (cardBaseline == null ? void 0 : cardBaseline.at) ? { ...cardBaseline.at } : normalizeResolvableValue(null, null),
above: normalizeBaselineDirectionConfig(void 0, cardBaseline == null ? void 0 : cardBaseline.above),
below: normalizeBaselineDirectionConfig(void 0, cardBaseline == null ? void 0 : cardBaseline.below)
};
if (rawBaseline === void 0) {
return inherited;
}
if (rawBaseline === null) {
return {
enabled: null,
at: normalizeResolvableValue(null, null),
above: { color: null },
below: { color: null }
};
}
if (typeof rawBaseline !== "object" || Array.isArray(rawBaseline)) {
return {
enabled: inherited.enabled,
at: normalizeStructuredResolvableValue(rawBaseline, inherited.at, null),
above: inherited.above,
below: inherited.below
};
}
return {
enabled: (_a = normalizeOptionalEnabled(rawBaseline.enabled)) != null ? _a : inherited.enabled,
at: normalizeStructuredResolvableValue(rawBaseline.at, inherited.at, null, { allowPercent: true }),
above: normalizeBaselineDirectionConfig(rawBaseline.above, inherited.above),
below: normalizeBaselineDirectionConfig(rawBaseline.below, inherited.below)
};
}
function inferSegmentEndValues(segments, fallbackEnd = null) {
const sorted = [...segments].sort((a, b) => a.from - b.from);
return sorted.map((segment, index) => {
var _a;
let to = Number.isFinite(segment.to) ? segment.to : null;
if (!Number.isFinite(to) && index < sorted.length - 1) {
to = sorted[index + 1].from;
}
if (!Number.isFinite(to) && Number.isFinite(fallbackEnd)) {
to = fallbackEnd;
}
return {
from: segment.from,
to,
color: segment.color,
label: (_a = segment.label) != null ? _a : null
};
});
}
function normalizeSeverityToSegments(input) {
if (!Array.isArray(input)) return null;
const segments = input.filter((segment) => Number.isFinite(segment == null ? void 0 : segment.from) && (segment == null ? void 0 : segment.color)).map((segment) => {
var _a;
return {
from: segment.from,
to: Number.isFinite(segment == null ? void 0 : segment.to) ? segment.to : null,
color: segment.color,
label: (_a = segment.label) != null ? _a : null
};
});
return inferSegmentEndValues(segments, 100);
}
function normalizeSegmentBoundary(input, legacySegmentSpace = null) {
var _a;
if (input === void 0) return { value: null, issue: null };
if (input === null) return { value: null, issue: "malformed" };
if (input && typeof input === "object" && !Array.isArray(input)) {
if (input.entity !== void 0 && input.entity !== null && input.entity !== "") {
return { value: null, issue: "entity" };
}
if (Object.prototype.hasOwnProperty.call(input, "percent")) {
const percent = getFiniteNumber(input.percent);
return Number.isFinite(percent) ? { value: normalizeResolvableValue(null, null, percent), issue: null } : { value: null, issue: "malformed" };
}
const fixed = getFiniteNumber((_a = input.fixed) != null ? _a : input.value);
return Number.isFinite(fixed) ? { value: normalizeResolvableValue(fixed, null), issue: null } : { value: null, issue: "malformed" };
}
if (typeof input === "string") {
const trimmed = input.trim();
if (looksLikeEntityId(trimmed)) return { value: null, issue: "entity" };
if (trimmed.includes("%")) {
const percent = parsePercentLiteral(trimmed);
return Number.isFinite(percent) ? { value: normalizeResolvableValue(null, null, percent), issue: null } : { value: null, issue: "malformed_percent" };
}
}
const numeric = getFiniteNumber(input);
if (!Number.isFinite(numeric)) return { value: null, issue: "malformed" };
return legacySegmentSpace === "percent" ? { value: normalizeResolvableValue(null, null, numeric), issue: null } : { value: normalizeResolvableValue(numeric, null), issue: null };
}
function normalizeGaugeSegments(input, options = {}) {
if (!Array.isArray(input)) return null;
const { legacySegmentSpace = null } = options;
const segments = input.map((segment) => {
var _a, _b;
const from = (segment == null ? void 0 : segment.from) === void 0 ? { value: null, issue: "malformed" } : normalizeSegmentBoundary(segment.from, legacySegmentSpace);
const to = (segment == null ? void 0 : segment.to) === void 0 ? { value: null, issue: null } : segment.to === null ? { value: null, issue: null } : normalizeSegmentBoundary(segment.to, legacySegmentSpace);
if (!(segment == null ? void 0 : segment.color)) {
return null;
}
return {
from: from.value,
to: to.value,
invalidBoundary: (_a = from.issue) != null ? _a : to.issue,
color: segment.color,
label: (_b = segment.label) != null ? _b : null
};
}).filter(Boolean);
return segments.map((segment, index) => {
var _a;
const nextValid = segments.slice(index + 1).find((candidate) => !candidate.invalidBoundary);
return {
from: segment.from ? { ...segment.from } : null,
to: segment.to ? { ...segment.to } : (nextValid == null ? void 0 : nextValid.from) ? { ...nextValid.from } : null,
...segment.invalidBoundary ? { invalidBoundary: segment.invalidBoundary } : {},
color: segment.color,
label: (_a = segment.label) != null ? _a : null
};
});
}
function normalizeScaleBound(entityConfig, cardConfig, key, defaultValue) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
const cardScale = cardConfig == null ? void 0 : cardConfig.scale;
const entityScale = entityConfig == null ? void 0 : entityConfig.scale;
const entityKey = `${key}_entity`;
const cardBound = cardScale == null ? void 0 : cardScale[key];
const inherited = cardBound ? normalizeResolvableValue(
(_b = (_a = cardBound.fixed) != null ? _a : cardBound.value) != null ? _b : null,
(_c = cardBound.entity) != null ? _c : null
) : normalizeResolvableValue(
(_d = cardConfig == null ? void 0 : cardConfig[key]) != null ? _d : defaultValue,
(_e = cardConfig == null ? void 0 : cardConfig[entityKey]) != null ? _e : null
);
const inheritedFixedExplicit = cardBound ? cardBound.fixed_explicit !== false && cardBound.fixed !== null && cardBound.fixed !== void 0 : (cardConfig == null ? void 0 : cardConfig[key]) !== null && (cardConfig == null ? void 0 : cardConfig[key]) !== void 0;
const withFixedExplicit = (bound, explicit) => {
Object.defineProperty(bound, "fixed_explicit", { value: explicit });
return bound;
};
if ((entityScale == null ? void 0 : entityScale[key]) !== void 0) {
const input = entityScale[key];
const fixedExplicit = looksLikeEntityId(input) ? inheritedFixedExplicit : typeof input === "object" && input !== null ? ((_f = input.fixed) != null ? _f : input.value) !== null && ((_g = input.fixed) != null ? _g : input.value) !== void 0 : input !== null;
return withFixedExplicit(normalizeStructuredResolvableValue(
entityScale[key],
inherited,
defaultValue
), fixedExplicit);
}
const entityOverride = entityConfig[entityKey];
const hasEntityOverride = entityOverride !== void 0 && entityOverride !== null;
const value = (_i = (_h = entityConfig[key]) != null ? _h : hasEntityOverride ? null : inherited.fixed) != null ? _i : inherited.entity ? null : defaultValue;
const entity = (_j = entityOverride != null ? entityOverride : inherited.entity) != null ? _j : null;
return withFixedExplicit(
normalizeResolvableValue(value, entity),
entityConfig[key] !== null && entityConfig[key] !== void 0 || !hasEntityOverride && inheritedFixedExplicit
);
}
function normalizeScaleConfig(entityConfig, cardConfig) {
return {
min: normalizeScaleBound(entityConfig, cardConfig, "min", 0),
max: normalizeScaleBound(entityConfig, cardConfig, "max", 100)
};
}
function fillStyleToColorMode(fillStyle) {
switch (fillStyle) {
case "solid":
return "single";
case "gradient":
return "gradient";
case "bands":
return "severity";
case "soft_bands":
return "severity";
case "band_gradient":
return "severity_gradient";
default:
return null;
}
}
function colorModeToFillStyle(colorMode) {
switch (colorMode) {
case "single":
return "solid";
case "gradient":
return "gradient";
case "severity":
return "bands";
case "severity_gradient":
return "band_gradient";
default:
return null;
}
}
function hasOwnConfigValue(config, key) {
return config !== null && config !== void 0 && Object.prototype.hasOwnProperty.call(config, key);
}
function hasExplicitColor(config) {
const values = [
hasOwnConfigValue(config, "color") ? config.color : void 0,
hasOwnConfigValue(config == null ? void 0 : config.bar, "color") ? config.bar.color : void 0
];
return values.some((value) => value !== void 0 && value !== null);
}
function getPaintExplicitness(config) {
const bar = config == null ? void 0 : config.bar;
const explicitPaintKeys = [
[config, "color_mode"],
[bar, "color_mode"],
[bar, "fill_style"],
[config, "severity"],
[config, "segments"],
[bar, "segments"],
[config, "gradient_stops"],
[bar, "gradient_stops"],
[bar, "solid_fill"]
];
return {
color: hasExplicitColor(config),
paint: explicitPaintKeys.some(([source, key]) => hasOwnConfigValue(source, key))
};
}
function normalizeBarModeConfig(barConfig = null, flatColorMode = null) {
var _a, _b, _c, _d, _e, _f;
const fillStyle = (_a = barConfig == null ? void 0 : barConfig.fill_style) != null ? _a : null;
const colorMode = (_c = (_b = barConfig == null ? void 0 : barConfig.color_mode) != null ? _b : flatColorMode) != null ? _c : null;
const normalizedColorMode = (_e = (_d = fillStyleToColorMode(fillStyle)) != null ? _d : colorMode) != null ? _e : "severity";
return {
fill_style: (_f = fillStyle != null ? fillStyle : colorModeToFillStyle(normalizedColorMode)) != null ? _f : "bands",
color_mode: normalizedColorMode
};
}
function resolveNormalizedBarMode(entityBar, entityConfig, cardBar, cardConfig, { scopeExplicitness = null, inheritedExplicitness = null, isCardScope = false } = {}) {
const colorOnlyScope = (scopeExplicitness == null ? void 0 : scopeExplicitness.color) === true && scopeExplicitness.paint !== true;
const inheritedPaintIsExplicit = (inheritedExplicitness == null ? void 0 : inheritedExplicitness.paint) === true;
if (colorOnlyScope && (isCardScope || !inheritedPaintIsExplicit)) {
return normalizeBarModeConfig({ fill_style: "solid" }, null);
}
if ((entityBar == null ? void 0 : entityBar.fill_style) !== void 0 || (entityBar == null ? void 0 : entityBar.color_mode) !== void 0 || entityConfig.color_mode !== void 0) {
return normalizeBarModeConfig(entityBar, entityConfig.color_mode);
}
if ((cardBar == null ? void 0 : cardBar.fill_style) !== void 0 || (cardBar == null ? void 0 : cardBar.color_mode) !== void 0 || (cardConfig == null ? void 0 : cardConfig.color_mode) !== void 0) {
return normalizeBarModeConfig(cardBar, cardConfig == null ? void 0 : cardConfig.color_mode);
}
return normalizeBarModeConfig(null, null);
}
function normalizeGradientStops(input) {
if (!Array.isArray(input)) return input != null ? input : null;
return input.map((stop) => {
if (!stop || typeof stop !== "object" || Array.isArray(stop)) {
return stop;
}
const percentPos = parsePercentLiteral(stop.pos);
const numericPos = Number.isFinite(percentPos) ? percentPos : getFiniteNumber(stop.pos);
return {
...stop,
pos: Number.isFinite(numericPos) ? numericPos : stop.pos
};
});
}
function normalizeNeedleConfig(input, inheritedNeedle = null) {
var _a, _b, _c, _d;
const base = inheritedNeedle ? { show: (_a = inheritedNeedle.show) != null ? _a : false, color: (_b = inheritedNeedle.color) != null ? _b : "#ffffff" } : { show: false, color: "#ffffff" };
if (input === void 0) {
return { ...base };
}
if (typeof input === "boolean") {
return {
show: input,
color: "#ffffff"
};
}
if (input && typeof input === "object" && !Array.isArray(input)) {
return {
show: (_c = input.show) != null ? _c : base.show,
color: (_d = input.color) != null ? _d : base.color
};
}
return { ...base };
}
function normalizeBarConfig(entityConfig, cardConfig, options = {}) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A;
const scopeExplicitness = (_a = options.scopeExplicitness) != null ? _a : getPaintExplicitness(entityConfig);
const inheritedExplicitness = (_c = (_b = options.inheritedExplicitness) != null ? _b : cardConfig == null ? void 0 : cardConfig[PAINT_EXPLICITNESS]) != null ? _c : getPaintExplicitness(cardConfig);
const isCardScope = (_d = options.isCardScope) != null ? _d : cardConfig == null;
const cardBar = cardConfig == null ? void 0 : cardConfig.bar;
const entityBar = entityConfig == null ? void 0 : entityConfig.bar;
const entityStructuredSegments = entityBar == null ? void 0 : entityBar.segments;
const entityTopLevelSegments = entityConfig.segments;
const entityLegacySeverity = entityConfig.severity;
const cardStructuredSegments = (_e = cardBar == null ? void 0 : cardBar.segments) != null ? _e : null;
const cardTopLevelSegments = (_f = cardConfig == null ? void 0 : cardConfig.segments) != null ? _f : null;
const cardLegacySeverity = (_g = cardConfig == null ? void 0 : cardConfig.severity) != null ? _g : null;
let segments = null;
let segment_space = (cardBar == null ? void 0 : cardBar.segment_space) === "percent" || (cardBar == null ? void 0 : cardBar.segment_space) === "scale" ? cardBar.segment_space : null;
if (entityStructuredSegments !== void 0 && entityStructuredSegments !== null) {
segment_space = (entityBar == null ? void 0 : entityBar.segment_space) === "percent" || (entityBar == null ? void 0 : entityBar.segment_space) === "scale" ? entityBar.segment_space : segment_space;
segments = normalizeGaugeSegments(entityStructuredSegments, { legacySegmentSpace: segment_space });
} else if (entityTopLevelSegments !== void 0 && entityTopLevelSegments !== null) {
segments = normalizeGaugeSegments(entityTopLevelSegments);
} else if (entityLegacySeverity !== void 0 && entityLegacySeverity !== null) {
segments = normalizeSeverityToSegments(entityLegacySeverity);
segment_space = "percent";
} else if (cardStructuredSegments !== null && cardStructuredSegments !== void 0) {
segments = cardStructuredSegments.map((segment) => ({ ...segment }));
} else if (cardTopLevelSegments !== null && cardTopLevelSegments !== void 0) {
segments = normalizeGaugeSegments(cardTopLevelSegments);
} else if (cardLegacySeverity !== null && cardLegacySeverity !== void 0) {
segments = normalizeSeverityToSegments(cardLegacySeverity);
segment_space = "percent";
}
const structuredAboveTargetColor = (entityConfig == null ? void 0 : entityConfig.target) && typeof entityConfig.target === "object" && !Array.isArray(entityConfig.target) ? (_h = entityConfig.target.when_exceeded) == null ? void 0 : _h.fill_color : void 0;
const inheritedStructuredAboveTargetColor = (cardConfig == null ? void 0 : cardConfig.target) && typeof cardConfig.target === "object" && !Array.isArray(cardConfig.target) ? (_i = cardConfig.target.when_exceeded) == null ? void 0 : _i.fill_color : void 0;
const normalizedMode = resolveNormalizedBarMode(
entityBar,
entityConfig,
cardBar,
cardConfig,
{ scopeExplicitness, inheritedExplicitness, isCardScope }
);
return {
fill_style: normalizedMode.fill_style,
color_mode: normalizedMode.color_mode,
needle: normalizeNeedleConfig(entityBar == null ? void 0 : entityBar.needle, cardBar == null ? void 0 : cardBar.needle),
solid_fill: (_k = (_j = entityBar == null ? void 0 : entityBar.solid_fill) != null ? _j : cardBar == null ? void 0 : cardBar.solid_fill) != null ? _k : false,
color: (_o = (_n = (_m = (_l = entityBar == null ? void 0 : entityBar.color) != null ? _l : entityConfig.color) != null ? _m : cardBar == null ? void 0 : cardBar.color) != null ? _n : cardConfig == null ? void 0 : cardConfig.color) != null ? _o : "#4a9eff",
gradient_stops: normalizeGradientStops(
(_s = (_r = (_q = (_p = entityBar == null ? void 0 : entityBar.gradient_stops) != null ? _p : entityConfig.gradient_stops) != null ? _q : cardBar == null ? void 0 : cardBar.gradient_stops) != null ? _r : cardConfig == null ? void 0 : cardConfig.gradient_stops) != null ? _s : null
),
severity: segments,
segments,
segment_space,
animated: (_w = (_v = (_u = (_t = entityBar == null ? void 0 : entityBar.animated) != null ? _t : entityConfig.animated) != null ? _u : cardBar == null ? void 0 : cardBar.animated) != null ? _v : cardConfig == null ? void 0 : cardConfig.animated) != null ? _w : true,
above_target_color: (_A = (_z = (_y = (_x = structuredAboveTargetColor != null ? structuredAboveTargetColor : entityConfig.above_target_color) != null ? _x : cardBar == null ? void 0 : cardBar.above_target_color) != null ? _y : inheritedStructuredAboveTargetColor) != null ? _z : cardConfig == null ? void 0 : cardConfig.above_target_color) != null ? _A : null
};
}
function clampSupportedRowHeight(height) {
return Math.max(24, height);
}
function normalizeLabelPosition(position, fallback = "left") {
const normalized = typeof position === "string" ? position.trim().toLowerCase() : "";
return ["left", "above", "inside", "off", "hero"].includes(normalized) ? normalized : fallback;
}
function normalizeHeroSize(size, fallback = "medium") {
const normalized = typeof size === "string" ? size.trim().toLowerCase() : "";
return ["small", "medium", "large"].includes(normalized) ? normalized : fallback;
}
function normalizeHeroFontSize(value) {
if (!Number.isFinite(value)) return null;
return Math.min(112, Math.max(12, value));
}
function normalizeLayoutConfig(entityConfig, cardConfig) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
const cardLayout = cardConfig == null ? void 0 : cardConfig.layout;
const entityLayout = entityConfig == null ? void 0 : entityConfig.layout;
const entityLabel = entityLayout == null ? void 0 : entityLayout.label;
const entityHero = entityLayout == null ? void 0 : entityLayout.hero;
const cardLabel = cardLayout == null ? void 0 : cardLayout.label;
const cardHero = cardLayout == null ? void 0 : cardLayout.hero;
const isCardLevelNormalization = !cardConfig;
const rawHeight = (_d = (_c = (_b = (_a = entityLayout == null ? void 0 : entityLayout.height) != null ? _a : entityConfig.height) != null ? _b : cardLayout == null ? void 0 : cardLayout.height) != null ? _c : cardConfig == null ? void 0 : cardConfig.height) != null ? _d : 38;
const heightExplicit = (entityLayout == null ? void 0 : entityLayout.height) !== void 0 || entityConfig._height_explicit === true || !isCardLevelNormalization && entityConfig.height !== void 0 || (cardLayout == null ? void 0 : cardLayout.height_explicit) === true || (cardConfig == null ? void 0 : cardConfig._height_explicit) === true;
const labelPosition = normalizeLabelPosition(
(_h = (_g = (_f = (_e = entityLabel == null ? void 0 : entityLabel.position) != null ? _e : entityConfig.label_position) != null ? _f : cardLabel == null ? void 0 : cardLabel.position) != null ? _g : cardLayout == null ? void 0 : cardLayout.label_position) != null ? _h : cardConfig == null ? void 0 : cardConfig.label_position,
"left"
);
const heroFontSize = labelPosition === "hero" ? normalizeHeroFontSize((_i = entityHero == null ? void 0 : entityHero.value_size) != null ? _i : cardHero == null ? void 0 : cardHero.value_size) : null;
return {
label: {
position: labelPosition,
width: (_n = (_m = (_l = (_k = (_j = entityLabel == null ? void 0 : entityLabel.width) != null ? _j : entityConfig.label_width) != null ? _k : cardLabel == null ? void 0 : cardLabel.width) != null ? _l : cardLayout == null ? void 0 : cardLayout.label_width) != null ? _m : cardConfig == null ? void 0 : cardConfig.label_width) != null ? _n : 100
},
hero: {
size: normalizeHeroSize(
(_y = (_x = (_w = (_v = (_u = (_t = (_s = (_r = (_q = (_p = (_o = entityHero == null ? void 0 : entityHero.size) != null ? _o : entityLabel == null ? void 0 : entityLabel.hero_size) != null ? _p : entityLabel == null ? void 0 : entityLabel.heroSize) != null ? _q : entityConfig.hero_size) != null ? _r : entityConfig.heroSize) != null ? _s : cardHero == null ? void 0 : cardHero.size) != null ? _t : cardLabel == null ? void 0 : cardLabel.hero_size) != null ? _u : cardLabel == null ? void 0 : cardLabel.heroSize) != null ? _v : cardLayout == null ? void 0 : cardLayout.hero_size) != null ? _w : cardLayout == null ? void 0 : cardLayout.heroSize) != null ? _x : cardConfig == null ? void 0 : cardConfig.hero_size) != null ? _y : cardConfig == null ? void 0 : cardConfig.heroSize,
"medium"
),
value_size: heroFontSize
},
height: clampSupportedRowHeight(rawHeight),
height_explicit: heightExplicit
};
}
function normalizeFormattingConfig(entityConfig, cardConfig) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const cardFormatting = cardConfig == null ? void 0 : cardConfig.formatting;
const entityFormatting = entityConfig == null ? void 0 : entityConfig.formatting;
return {
decimal: (_d = (_c = (_b = (_a = entityFormatting == null ? void 0 : entityFormatting.decimal) != null ? _a : entityConfig.decimal) != null ? _b : cardFormatting == null ? void 0 : cardFormatting.decimal) != null ? _c : cardConfig == null ? void 0 : cardConfig.decimal) != null ? _d : null,
unit: (_h = (_g = (_f = (_e = entityFormatting == null ? void 0 : entityFormatting.unit) != null ? _e : entityConfig.unit) != null ? _f : cardFormatting == null ? void 0 : cardFormatting.unit) != null ? _g : cardConfig == null ? void 0 : cardConfig.unit) != null ? _h : null
};
}
function normalizeTargetMarkerShape(value) {
const normalized = String(value != null ? value : "").trim().toLowerCase();
return normalized === "triangle" || normalized === "diamond" ? normalized : "diamond";
}
function normalizeMarkerDirection(value) {
const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
return normalized === "outward" ? "outward" : "inward";
}
function normalizeMarkerLabelConfig(rawLabel, inherited = {}) {
var _a, _b, _c;
const label = rawLabel && typeof rawLabel === "object" && !Array.isArray(rawLabel) ? rawLabel : {};
const hasText = Object.prototype.hasOwnProperty.call(label, "text");
const textValue = hasText ? typeof label.text === "string" ? label.text.replace(/\s+/g, " ").trim() : inherited.label_text : inherited.label_text;
const hasPrecision = Object.prototype.hasOwnProperty.call(label, "precision");
const hasDecimal = Object.prototype.hasOwnProperty.call(label, "decimal");
const precisionValue = hasPrecision ? label.precision : label.decimal;
const precisionSet = hasPrecision || hasDecimal;
const precision = precisionSet ? getFiniteNumber(precisionValue) : (_a = inherited.label_precision) != null ? _a : null;
const precisionInvalid = precisionSet && precisionValue !== null && precisionValue !== "" && !(Number.isInteger(precision) && precision >= 0);
return {
label_text: textValue != null ? textValue : null,
label_show_value: typeof label.show_value === "boolean" ? label.show_value : (_b = inherited.label_show_value) != null ? _b : true,
label_show_unit: typeof label.show_unit === "boolean" ? label.show_unit : (_c = inherited.label_show_unit) != null ? _c : true,
label_precision: precisionInvalid ? null : precision,
label_invalid_show: label.show !== void 0 && typeof label.show !== "boolean",
label_invalid_text: hasText && typeof label.text !== "string",
label_invalid_show_value: label.show_value !== void 0 && typeof label.show_value !== "boolean",
label_invalid_show_unit: label.show_unit !== void 0 && typeof label.show_unit !== "boolean",
label_invalid_precision: precisionInvalid,
label_precision_key: precisionInvalid ? hasPrecision ? "precision" : "decimal" : void 0,
label_unsupported_unit: Object.prototype.hasOwnProperty.call(label, "unit")
};
}
function builtinMarkerLabelFields(options) {
const fields = {};
if (options.label_text !== null && options.label_text !== void 0) fields.label_text = options.label_text;
if (options.label_show_value === false) fields.label_show_value = false;
if (options.label_show_unit === false) fields.label_show_unit = false;
if (options.label_precision !== null && options.label_precision !== void 0) fields.label_precision = options.label_precision;
if (options.label_invalid_text) fields.label_invalid_text = true;
if (options.label_invalid_show_value) fields.label_invalid_show_value = true;
if (options.label_invalid_show_unit) fields.label_invalid_show_unit = true;
if (options.label_invalid_show) fields.label_invalid_show = true;
if (options.label_invalid_precision) fields.label_invalid_precision = true;
if (options.label_invalid_precision && options.label_precision_key) fields.label_precision_key = options.label_precision_key;
if (options.label_unsupported_unit) fields.label_unsupported_unit = true;
return fields;
}
function isInvalidMarkerDirection(value) {
return value !== void 0 && !["inward", "outward"].includes(
typeof value === "string" ? value.trim().toLowerCase() : ""
);
}
function normalizeTargetMarkerConfig(entityConfig, cardConfig) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I;
const cardTarget = cardConfig == null ? void 0 : cardConfig.target_marker;
const rawTarget = entityConfig == null ? void 0 : entityConfig.target;
const legacyCardTarget = (cardConfig == null ? void 0 : cardConfig.target) && typeof cardConfig.target === "object" && !Array.isArray(cardConfig.target) ? null : (_a = cardConfig == null ? void 0 : cardConfig.target) != null ? _a : null;
const inheritedTarget = cardTarget ? {
...cardTarget,
shape: normalizeTargetMarkerShape(cardTarget.shape),
direction: normalizeMarkerDirection(cardTarget.direction)
} : {
enabled: null,
source: normalizeResolvableValue(null, null),
color: (_b = cardConfig == null ? void 0 : cardConfig.target_color) != null ? _b : "#888",
show_label: (_c = cardConfig == null ? void 0 : cardConfig.show_target_label) != null ? _c : false,
...builtinMarkerLabelFields(normalizeMarkerLabelConfig((_d = cardConfig == null ? void 0 : cardConfig.target) == null ? void 0 : _d.label)),
label_decimal: (_j = (_i = (_f = (_e = cardConfig == null ? void 0 : cardConfig.target) == null ? void 0 : _e.label) == null ? void 0 : _f.precision) != null ? _i : (_h = (_g = cardConfig == null ? void 0 : cardConfig.target) == null ? void 0 : _g.label) == null ? void 0 : _h.decimal) != null ? _j : null,
shape: "diamond",
direction: "inward"
};
if (rawTarget && typeof rawTarget === "object" && !Array.isArray(rawTarget)) {
const labelOptions = normalizeMarkerLabelConfig(rawTarget.label, inheritedTarget);
const normalizedTarget2 = {
enabled: (_l = (_k = normalizeOptionalEnabled(rawTarget.enabled)) != null ? _k : inheritedTarget.enabled) != null ? _l : null,
source: normalizeStructuredResolvableValue(rawTarget.at, inheritedTarget.source, null, { allowPercent: true }),
color: (_n = (_m = rawTarget.color) != null ? _m : entityConfig.target_color) != null ? _n : inheritedTarget.color,
show_label: (_q = (_p = (_o = rawTarget.label) == null ? void 0 : _o.show) != null ? _p : entityConfig.show_target_label) != null ? _q : inheritedTarget.show_label,
shape: Object.prototype.hasOwnProperty.call(rawTarget, "shape") ? normalizeTargetMarkerShape(rawTarget.shape) : inheritedTarget.shape,
direction: Object.prototype.hasOwnProperty.call(rawTarget, "direction") ? normalizeMarkerDirection(rawTarget.direction) : inheritedTarget.direction,
...isInvalidMarkerDirection(rawTarget.direction) ? { direction_invalid: true } : {},
...builtinMarkerLabelFields(labelOptions)
};
const labelDecimal2 = labelOptions.label_precision;
if (labelDecimal2 !== null && labelDecimal2 !== void 0) {
normalizedTarget2.label_decimal = labelDecimal2;
normalizedTarget2.label_precision = labelDecimal2;
}
return normalizedTarget2;
}
const value = (_v = (_u = (_s = entityConfig.target) != null ? _s : (_r = inheritedTarget.source) == null ? void 0 : _r.fixed) != null ? _u : (_t = inheritedTarget.source) == null ? void 0 : _t.value) != null ? _v : legacyCardTarget;
const entity = (_z = (_y = (_x = entityConfig.target_entity) != null ? _x : (_w = inheritedTarget.source) == null ? void 0 : _w.entity) != null ? _y : cardConfig == null ? void 0 : cardConfig.target_entity) != null ? _z : null;
const percent = entityConfig.target === void 0 && entityConfig.target_entity === void 0 ? (_B = (_A = inheritedTarget.source) == null ? void 0 : _A.percent) != null ? _B : null : null;
const normalizedTarget = {
enabled: (_C = inheritedTarget.enabled) != null ? _C : null,
source: normalizeResolvableValue(value, entity, percent),
color: (_F = (_E = (_D = entityConfig.target_color) != null ? _D : inheritedTarget.color) != null ? _E : cardConfig == null ? void 0 : cardConfig.target_color) != null ? _F : "#888",
show_label: (_I = (_H = (_G = entityConfig.show_target_label) != null ? _G : inheritedTarget.show_label) != null ? _H : cardConfig == null ? void 0 : cardConfig.show_target_label) != null ? _I : false,
shape: inheritedTarget.shape,
direction: inheritedTarget.direction,
...builtinMarkerLabelFields(inheritedTarget)
};
const labelDecimal = normalizedTarget.label_precision;
if (labelDecimal !== null && labelDecimal !== void 0) {
normalizedTarget.label_decimal = labelDecimal;
}
return normalizedTarget;
}
function getRawExtremumConfig(config, key) {
const value = config == null ? void 0 : config[key];
if (value && typeof value === "object" && !Array.isArray(value)) return value;
const markerValue = config == null ? void 0 : config[`${key}_marker`];
return markerValue && typeof markerValue === "object" && !Array.isArray(markerValue) ? markerValue : null;
}
function normalizeLabelConfig(rawConfig, inheritedConfig) {
var _a, _b, _c;
const rawLabel = rawConfig == null ? void 0 : rawConfig.label;
const hasRawLabel = rawLabel && typeof rawLabel === "object" && !Array.isArray(rawLabel);
const options = normalizeMarkerLabelConfig(rawLabel, inheritedConfig);
return {
show: hasRawLabel && typeof rawLabel.show === "boolean" ? rawLabel.show : (_a = inheritedConfig.show_label) != null ? _a : false,
text: options.label_text,
invalidShow: options.label_invalid_show,
showValue: options.label_show_value,
showUnit: options.label_show_unit,
precision: options.label_invalid_precision ? null : (_c = (_b = options.label_precision) != null ? _b : inheritedConfig.label_decimal) != null ? _c : null,
invalidText: options.label_invalid_text,
invalidShowValue: options.label_invalid_show_value,
invalidShowUnit: options.label_invalid_show_unit,
invalidPrecision: options.label_invalid_precision,
precisionKey: options.label_precision_key,
unsupportedUnit: options.label_unsupported_unit
};
}
function normalizeExtremumMarkerConfig(entityConfig, cardConfig, key, options = {}) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
const { legacy = false, defaultColor = "#888888" } = options;
const cardMarker = cardConfig == null ? void 0 : cardConfig[`${key}_marker`];
const rawMarker = getRawExtremumConfig(entityConfig, key);
const inherited = cardMarker != null ? cardMarker : {
show: legacy ? (_a = cardConfig == null ? void 0 : cardConfig.show_peak) != null ? _a : false : false,
color: legacy ? (_b = cardConfig == null ? void 0 : cardConfig.peak_color) != null ? _b : defaultColor : defaultColor,
show_label: false,
label_decimal: null,
label_text: null,
label_show_value: true,
label_show_unit: true,
label_precision: null,
reset: { kind: "never" },
direction: "inward"
};
const hasReset = rawMarker && Object.prototype.hasOwnProperty.call(rawMarker, "reset");
const rawReset = hasReset ? rawMarker.reset : void 0;
const reset = hasReset ? normalizeReset(rawReset) : (_c = inherited.reset) != null ? _c : { kind: "never" };
const rawLabel = rawMarker == null ? void 0 : rawMarker.label;
const label = normalizeLabelConfig(rawMarker, inherited);
const normalized = {
show: (_f = (_e = (_d = rawMarker == null ? void 0 : rawMarker.enabled) != null ? _d : legacy ? entityConfig.show_peak : void 0) != null ? _e : inherited.show) != null ? _f : false,
color: (_i = (_h = (_g = rawMarker == null ? void 0 : rawMarker.color) != null ? _g : legacy ? entityConfig.peak_color : void 0) != null ? _h : inherited.color) != null ? _i : defaultColor,
direction: rawMarker && Object.prototype.hasOwnProperty.call(rawMarker, "direction") ? normalizeMarkerDirection(rawMarker.direction) : normalizeMarkerDirection(inherited.direction),
...isInvalidMarkerDirection(rawMarker == null ? void 0 : rawMarker.direction) ? { direction_invalid: true } : {}
};
const inheritedAdvanced = cardMarker && (Object.prototype.hasOwnProperty.call(cardMarker, "show_label") || Object.prototype.hasOwnProperty.call(cardMarker, "label_decimal") || Object.prototype.hasOwnProperty.call(cardMarker, "label_text") || Object.prototype.hasOwnProperty.call(cardMarker, "label_show_value") || Object.prototype.hasOwnProperty.call(cardMarker, "label_show_unit") || Object.prototype.hasOwnProperty.call(cardMarker, "label_precision") || Object.prototype.hasOwnProperty.call(cardMarker, "reset"));
const hasAdvancedConfig = key === "floor" || hasReset || rawLabel !== void 0 || inheritedAdvanced;
if (hasAdvancedConfig) {
normalized.show_label = label.show;
normalized.label_decimal = label.precision;
normalized.reset = reset;
}
Object.assign(normalized, builtinMarkerLabelFields({
label_text: label.text,
label_show_value: label.showValue === true ? void 0 : label.showValue,
label_show_unit: label.showUnit === true ? void 0 : label.showUnit,
label_precision: label.precision,
label_invalid_text: label.invalidText,
label_invalid_show: label.invalidShow,
label_invalid_show_value: label.invalidShowValue,
label_invalid_show_unit: label.invalidShowUnit,
label_invalid_precision: label.invalidPrecision,
label_precision_key: label.precisionKey,
label_unsupported_unit: label.unsupportedUnit
}));
if (hasReset && !isValidReset(rawReset)) {
normalized.reset_invalid = true;
}
return normalized;
}
function normalizePeakMarkerConfig(entityConfig, cardConfig) {
return normalizeExtremumMarkerConfig(entityConfig, cardConfig, "peak", { legacy: true, defaultColor: "#888888" });
}
function normalizeFloorMarkerConfig(entityConfig, cardConfig) {
return normalizeExtremumMarkerConfig(entityConfig, cardConfig, "floor", { defaultColor: "#888888" });
}
function normalizeEntityConfig(entityConfig, cardConfig) {
var _a, _b, _c;
const normalizedEntity = {
...entityConfig,
_normalized: true,
entity: entityConfig.entity,
name: (_a = entityConfig.name) != null ? _a : null,
icon: entityConfig.icon
};
normalizedEntity.layout = normalizeLayoutConfig(entityConfig, cardConfig);
normalizedEntity.scale = normalizeScaleConfig(entityConfig, cardConfig);
normalizedEntity.bar = normalizeBarConfig(entityConfig, cardConfig, {
scopeExplicitness: getPaintExplicitness(entityConfig),
inheritedExplicitness: (_b = cardConfig == null ? void 0 : cardConfig[PAINT_EXPLICITNESS]) != null ? _b : getPaintExplicitness(cardConfig),
isCardScope: false
});
normalizedEntity.baseline = normalizeBaselineConfig(entityConfig, cardConfig);
normalizedEntity.formatting = normalizeFormattingConfig(entityConfig, cardConfig);
normalizedEntity.target_marker = normalizeTargetMarkerConfig(entityConfig, cardConfig);
normalizedEntity.peak_marker = normalizePeakMarkerConfig(entityConfig, cardConfig);
normalizedEntity.floor_marker = normalizeFloorMarkerConfig(entityConfig, cardConfig);
const normalizedMarkers = entityConfig.markers === void 0 ? {
markers: applyGenericMarkerCapacity((_c = cardConfig == null ? void 0 : cardConfig.generic_markers) != null ? _c : [], normalizedEntity),
invalidList: (cardConfig == null ? void 0 : cardConfig.generic_markers_invalid) === true
} : normalizeGenericMarkerList(entityConfig.markers, normalizedEntity);
normalizedEntity.generic_markers = normalizedMarkers.markers;
normalizedEntity.generic_markers_invalid = normalizedMarkers.invalidList;
normalizedEntity.min = normalizedEntity.scale.min.fixed;
normalizedEntity.min_entity = normalizedEntity.scale.min.entity;
normalizedEntity.max = normalizedEntity.scale.max.fixed;
normalizedEntity.max_entity = normalizedEntity.scale.max.entity;
normalizedEntity.height = normalizedEntity.layout.height;
normalizedEntity.label_position = normalizedEntity.layout.label.position;
normalizedEntity.label_width = normalizedEntity.layout.label.width;
normalizedEntity.color_mode = normalizedEntity.bar.color_mode;
normalizedEntity.fill_style = normalizedEntity.bar.fill_style;
normalizedEntity.solid_fill = normalizedEntity.bar.solid_fill;
normalizedEntity.color = normalizedEntity.bar.color;
normalizedEntity.gradient_stops = normalizedEntity.bar.gradient_stops;
normalizedEntity.severity = normalizedEntity.bar.severity;
normalizedEntity.animated = normalizedEntity.bar.animated;
normalizedEntity.above_target_color = normalizedEntity.bar.above_target_color;
normalizedEntity.decimal = normalizedEntity.formatting.decimal;
normalizedEntity.unit = normalizedEntity.formatting.unit;
normalizedEntity.target = normalizedEntity.target_marker.source.fixed;
normalizedEntity.target_entity = normalizedEntity.target_marker.source.entity;
normalizedEntity.target_color = normalizedEntity.target_marker.color;
normalizedEntity.show_target_label = normalizedEntity.target_marker.show_label;
normalizedEntity.show_peak = normalizedEntity.peak_marker.show;
normalizedEntity.peak_color = normalizedEntity.peak_marker.color;
return normalizedEntity;
}
function normalizeCardConfig(rawConfig) {
var _a;
const cardPaintExplicitness = getPaintExplicitness(rawConfig);
const baseConfig = {
title: "",
label_position: "left",
color_mode: "severity",
color: "#4a9eff",
animated: true,
show_peak: false,
peak_color: "#888888",
target: null,
target_entity: null,
target_color: "#888",
show_target_label: false,
above_target_color: null,
baseline: null,
decimal: null,
gradient_stops: null,
min: 0,
min_entity: null,
max: 100,
max_entity: null,
height: 38,
label_width: 100,
severity: [
{ from: 0, to: 33, color: "#4CAF50" },
{ from: 33, to: 75, color: "#FF9800" },
{ from: 75, to: 100, color: "#F44336" }
],
...rawConfig
};
baseConfig._height_explicit = ((_a = rawConfig == null ? void 0 : rawConfig.layout) == null ? void 0 : _a.height) !== void 0 || (rawConfig == null ? void 0 : rawConfig.height) !== void 0;
if (baseConfig.entity && !baseConfig.entities) {
baseConfig.entities = [{
entity: baseConfig.entity,
...baseConfig.name !== void 0 ? { name: baseConfig.name } : {}
}];
}
baseConfig.entities = baseConfig.entities.map(
(e) => typeof e === "string" ? { entity: e } : e
);
const normalizedCard = {
...baseConfig,
_normalized: true
};
Object.defineProperty(normalizedCard, PAINT_EXPLICITNESS, {
value: cardPaintExplicitness,
enumerable: false
});
normalizedCard.layout = normalizeLayoutConfig(baseConfig, null);
normalizedCard.scale = normalizeScaleConfig(rawConfig, null);
normalizedCard.bar = normalizeBarConfig(baseConfig, null, {
scopeExplicitness: cardPaintExplicitness,
inheritedExplicitness: null,
isCardScope: true
});
normalizedCard.baseline = normalizeBaselineConfig(baseConfig, null);
normalizedCard.formatting = normalizeFormattingConfig(baseConfig, null);
normalizedCard.target_marker = normalizeTargetMarkerConfig(baseConfig, null);
normalizedCard.peak_marker = normalizePeakMarkerConfig(baseConfig, null);
normalizedCard.floor_marker = normalizeFloorMarkerConfig(baseConfig, null);
const normalizedMarkers = normalizeGenericMarkerList(baseConfig.markers, normalizedCard);
normalizedCard.generic_markers = normalizedMarkers.markers;
normalizedCard.generic_markers_invalid = normalizedMarkers.invalidList;
normalizedCard.entities = baseConfig.entities.map(
(entityCfg) => normalizeEntityConfig(entityCfg, normalizedCard)
);
return normalizedCard;
}
var PAINT_EXPLICITNESS;
var init_normalize = __esm({
"src/config/normalize.js"() {
init_extrema();
PAINT_EXPLICITNESS = /* @__PURE__ */ Symbol("sbcp.paintExplicitness");
}
});
// src/config/resolve.js
function getEntityNumericValue(hass, entityId) {
var _a;
if (!entityId || !((_a = hass == null ? void 0 : hass.states) == null ? void 0 : _a[entityId])) return null;
const raw = hass.states[entityId].state;
const num = parseFloat(raw);
return Number.isFinite(num) ? num : null;
}
function getNumericValue(hass, value, entityId = null) {
const entityValue = getEntityNumericValue(hass, entityId);
if (entityValue !== null) return entityValue;
if (value === null || value === void 0 || value === "") return null;
const num = parseFloat(value);
return Number.isFinite(num) ? num : null;
}
function resolvePercentValue(percent, minValue, maxValue) {
if (!Number.isFinite(percent)) return null;
const safeMin = Number.isFinite(minValue) ? minValue : 0;
const safeMax = Number.isFinite(maxValue) ? maxValue : 100;
return safeMin + percent / 100 * (safeMax - safeMin);
}
function getNormalizedResolvableNumericValue(hass, resolvable, minValue = null, maxValue = null) {
var _a;
if (!resolvable) return null;
const entityValue = getEntityNumericValue(hass, resolvable.entity);
if (entityValue !== null) return entityValue;
const fixedValue = getNumericValue(hass, (_a = resolvable.fixed) != null ? _a : resolvable.value, null);
if (fixedValue !== null) return fixedValue;
if (Number.isFinite(resolvable.percent)) {
return resolvePercentValue(resolvable.percent, minValue, maxValue);
}
return null;
}
function getResolvedScale(hass, scale, previousScale = null) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
const isValid = (bounds) => Number.isFinite(bounds == null ? void 0 : bounds.min) && Number.isFinite(bounds == null ? void 0 : bounds.max) && bounds.min < bounds.max;
const fixed = {
min: getNumericValue(null, (_c = (_a = scale == null ? void 0 : scale.min) == null ? void 0 : _a.fixed) != null ? _c : (_b = scale == null ? void 0 : scale.min) == null ? void 0 : _b.value),
max: getNumericValue(null, (_f = (_d = scale == null ? void 0 : scale.max) == null ? void 0 : _d.fixed) != null ? _f : (_e = scale == null ? void 0 : scale.max) == null ? void 0 : _e.value)
};
if (((_g = scale == null ? void 0 : scale.min) == null ? void 0 : _g.entity) && ((_h = scale == null ? void 0 : scale.max) == null ? void 0 : _h.entity)) {
const dynamic = {
min: getEntityNumericValue(hass, scale.min.entity),
max: getEntityNumericValue(hass, scale.max.entity)
};
if (isValid(dynamic)) return dynamic;
if (Number.isFinite(dynamic.min) && Number.isFinite(dynamic.max) && isValid(previousScale)) {
return previousScale;
}
return scale.min.fixed_explicit !== false && scale.max.fixed_explicit !== false && isValid(fixed) ? fixed : { min: 0, max: 100 };
}
const resolved = {
min: (_i = getNormalizedResolvableNumericValue(hass, scale == null ? void 0 : scale.min)) != null ? _i : 0,
max: (_j = getNormalizedResolvableNumericValue(hass, scale == null ? void 0 : scale.max)) != null ? _j : 100
};
if (isValid(resolved)) return resolved;
const fallback = {
min: (_k = fixed.min) != null ? _k : 0,
max: (_l = fixed.max) != null ? _l : 100
};
return isValid(fallback) ? fallback : { min: 0, max: 100 };
}
var init_resolve = __esm({
"src/config/resolve.js"() {
init_normalize();
}
});
// src/config/validate.js
function createDiagnostic(code, message, path, entity = null) {
return { code, message, path, entity };
}
function getStaticFixedValue(resolvable) {
var _a;
if (!resolvable || resolvable.entity || Number.isFinite(resolvable.percent)) {
return null;
}
return getFiniteNumber((_a = resolvable.fixed) != null ? _a : resolvable.value);
}
function addWarning(diagnostics, code, message, path, entity = null) {
diagnostics.warnings.push(createDiagnostic(code, message, path, entity));
}
function validateScaleBounds(diagnostics, scale, path, entity = null) {
var _a, _b;
if (((_a = scale == null ? void 0 : scale.min) == null ? void 0 : _a.entity) && ((_b = scale == null ? void 0 : scale.max) == null ? void 0 : _b.entity)) {
const hasFixed = (bound) => {
var _a2, _b2;
return bound.fixed_explicit !== false && ((_a2 = bound.fixed) != null ? _a2 : bound.value) !== null && ((_b2 = bound.fixed) != null ? _b2 : bound.value) !== void 0;
};
if (hasFixed(scale.min) !== hasFixed(scale.max)) {
addWarning(
diagnostics,
"scale.orphan_fixed_fallback",
"Both dynamic scale bounds require a complete fixed fallback pair. The single fixed fallback will not be used if the dynamic pair becomes unavailable.",
`${path}.scale`,
entity
);
}
}
const min = getStaticFixedValue(scale == null ? void 0 : scale.min);
const max = getStaticFixedValue(scale == null ? void 0 : scale.max);
if (Number.isFinite(min) && Number.isFinite(max) && min > max) {
addWarning(diagnostics, "scale.min_gt_max", "Scale minimum is greater than maximum.", path, entity);
}
return { min, max };
}
function validateTargetRange(diagnostics, config, scaleBounds, path, entity = null) {
var _a;
const target = getStaticFixedValue((_a = config == null ? void 0 : config.target_marker) == null ? void 0 : _a.source);
if (!Number.isFinite(target)) return;
const { min, max } = scaleBounds;
if (Number.isFinite(min) && Number.isFinite(max) && (target < min || target > max)) {
addWarning(diagnostics, "target.outside_scale", "Fixed target value is outside the fixed scale range.", path, entity);
}
}
function validateBaselineRange(diagnostics, config, scaleBounds, path, entity = null) {
var _a;
const baseline = getStaticFixedValue((_a = config == null ? void 0 : config.baseline) == null ? void 0 : _a.at);
if (!Number.isFinite(baseline)) return;
const { min, max } = scaleBounds;
if (Number.isFinite(min) && Number.isFinite(max) && (baseline < min || baseline > max)) {
addWarning(diagnostics, "baseline.outside_scale", "Fixed baseline value is outside the fixed scale range.", path, entity);
}
}
function hasConfiguredResolvableValue(resolvable) {
var _a;
return !!resolvable && (Number.isFinite(getFiniteNumber((_a = resolvable.fixed) != null ? _a : resolvable.value)) || Number.isFinite(resolvable.percent) || !!resolvable.entity);
}
function validateBaselineSuppressesNeedle(diagnostics, config, path, entity = null) {
var _a, _b, _c, _d;
const needleEnabled = ((_b = (_a = config == null ? void 0 : config.bar) == null ? void 0 : _a.needle) == null ? void 0 : _b.show) === true;
const baselineConfigured = ((_c = config == null ? void 0 : config.baseline) == null ? void 0 : _c.enabled) !== false && hasConfiguredResolvableValue((_d = config == null ? void 0 : config.baseline) == null ? void 0 : _d.at);
if (needleEnabled && baselineConfigured) {
addWarning(
diagnostics,
"baseline-suppresses-needle",
"Baseline rendering suppresses the needle marker.",
path,
entity
);
}
}
function validateExtremumReset(diagnostics, config, path, entity = null) {
var _a;
for (const marker of ["peak_marker", "floor_marker"]) {
if ((_a = config == null ? void 0 : config[marker]) == null ? void 0 : _a.reset_invalid) {
addWarning(
diagnostics,
`${marker}.invalid_reset`,
"Invalid marker reset; using never.",
`${path}.${marker}.reset`,
entity
);
}
}
}
function validateMarkerDirections(diagnostics, config, path, entity = null) {
var _a;
for (const marker of ["target_marker", "peak_marker", "floor_marker"]) {
if ((_a = config == null ? void 0 : config[marker]) == null ? void 0 : _a.direction_invalid) {
addWarning(
diagnostics,
`${marker}.invalid_direction`,
"Invalid marker direction; using inward.",
`${path}.${marker === "target_marker" ? "target" : marker.replace("_marker", "")}.direction`,
entity
);
}
}
}
function validateBuiltinMarkerLabels(diagnostics, config, path, entity = null) {
var _a;
for (const [key, publicKey] of [["target_marker", "target"], ["peak_marker", "peak"], ["floor_marker", "floor"]]) {
const marker = config == null ? void 0 : config[key];
const labelPath = `${path}.${publicKey}.label`;
if (marker == null ? void 0 : marker.label_invalid_show) addWarning(diagnostics, "markers.invalid_label_show", "Marker label show must be a boolean; using the existing fallback.", `${labelPath}.show`, entity);
if (marker == null ? void 0 : marker.label_invalid_text) addWarning(diagnostics, "markers.invalid_label_text", "Marker label text must be a string; ignoring it.", `${labelPath}.text`, entity);
if (marker == null ? void 0 : marker.label_invalid_show_value) addWarning(diagnostics, "markers.invalid_label_show_value", "Marker label show_value must be a boolean; using the inherited or default value.", `${labelPath}.show_value`, entity);
if (marker == null ? void 0 : marker.label_invalid_show_unit) addWarning(diagnostics, "markers.invalid_label_show_unit", "Marker label show_unit must be a boolean; using the inherited or default value.", `${labelPath}.show_unit`, entity);
if (marker == null ? void 0 : marker.label_invalid_precision) addWarning(diagnostics, "markers.invalid_label_precision", "Marker label precision must be a non-negative integer; using the inherited or row precision.", `${labelPath}.${(_a = marker.label_precision_key) != null ? _a : "precision"}`, entity);
if (marker == null ? void 0 : marker.label_unsupported_unit) addWarning(diagnostics, "markers.unsupported_label_unit", "Marker label unit is no longer supported; use show_unit instead.", `${labelPath}.unit`, entity);
}
}
function validateGenericMarkers(diagnostics, markers, invalidList, path, entity = null) {
var _a, _b, _c, _d, _e, _f, _g, _h;
if (invalidList) {
addWarning(diagnostics, "markers.invalid_list", "Markers must be a list; ignoring the malformed value.", path, entity);
}
for (const marker of markers != null ? markers : []) {
const markerPath = `${path}[${marker.index}]`;
if (marker.malformed) {
addWarning(diagnostics, "markers.invalid_item", "Marker must be an object; ignoring this item.", markerPath, entity);
continue;
}
if (marker.invalidSource && !marker.invalidPercentage && !marker.unsupportedPercentField) {
addWarning(diagnostics, "markers.invalid_source", "Marker requires a valid fixed value, percentage, or entity source.", `${markerPath}.at`, entity);
}
if (marker.invalidPercentage) {
addWarning(diagnostics, "markers.invalid_percentage", "Marker percentage must be between 0 and 100 inclusive.", `${markerPath}.at`, entity);
}
if (marker.unsupportedPercentField) {
addWarning(diagnostics, "markers.invalid_source", 'Use a percentage string such as "35%" instead of an at.percent field.', `${markerPath}.at`, entity);
}
if (marker.invalidLane) {
addWarning(diagnostics, "markers.invalid_lane", "Marker lane must be above or below; ignoring this marker.", `${markerPath}.lane`, entity);
}
if (marker.invalidShape) {
addWarning(diagnostics, "markers.invalid_shape", "Invalid marker shape; using circle.", `${markerPath}.shape`, entity);
}
if (marker.invalidDirection) {
addWarning(diagnostics, "markers.invalid_direction", "Invalid marker direction; using inward.", `${markerPath}.direction`, entity);
}
if (marker.invalidShowMarker) addWarning(diagnostics, "markers.invalid_show_marker", "Marker show_marker must be a boolean; using true.", `${markerPath}.show_marker`, entity);
if ((_a = marker.label) == null ? void 0 : _a.invalidEntity) addWarning(diagnostics, "markers.invalid_label_entity", "Marker label entity must be a valid entity ID; ignoring it.", `${markerPath}.label.entity`, entity);
if ((_b = marker.label) == null ? void 0 : _b.invalidShow) addWarning(diagnostics, "markers.invalid_label_show", "Marker label show must be a boolean; using the existing fallback.", `${markerPath}.label.show`, entity);
if ((_c = marker.label) == null ? void 0 : _c.invalidText) addWarning(diagnostics, "markers.invalid_label_text", "Marker label text must be a string; ignoring it.", `${markerPath}.label.text`, entity);
if ((_d = marker.label) == null ? void 0 : _d.invalidShowValue) addWarning(diagnostics, "markers.invalid_label_show_value", "Marker label show_value must be a boolean; using the inherited or default value.", `${markerPath}.label.show_value`, entity);
if ((_e = marker.label) == null ? void 0 : _e.invalidShowUnit) addWarning(diagnostics, "markers.invalid_label_show_unit", "Marker label show_unit must be a boolean; using the inherited or default value.", `${markerPath}.label.show_unit`, entity);
if ((_f = marker.label) == null ? void 0 : _f.invalidPrecision) addWarning(diagnostics, "markers.invalid_label_precision", "Marker label precision must be a non-negative integer; using the inherited or row precision.", `${markerPath}.label.${(_g = marker.label.invalidPrecisionKey) != null ? _g : "precision"}`, entity);
if ((_h = marker.label) == null ? void 0 : _h.unsupportedUnit) addWarning(diagnostics, "markers.unsupported_label_unit", "Marker label unit is no longer supported; use show_unit instead.", `${markerPath}.label.unit`, entity);
if (marker.valid && !marker.accepted) {
addWarning(diagnostics, "markers.excess_capacity", "This marker is not rendered because its lane already has four markers.", markerPath, entity);
}
}
}
function getStaticSegmentBound(boundary) {
var _a;
if (!boundary || boundary.entity || Number.isFinite(boundary.percent)) return null;
return getFiniteNumber((_a = boundary.fixed) != null ? _a : boundary.value);
}
function validateSegments(diagnostics, segments, scaleBounds, path, entity = null) {
if (!Array.isArray(segments) || !segments.length) return;
const staticSegments = [];
const { min, max } = scaleBounds;
for (let index = 0; index < segments.length; index += 1) {
const segment = segments[index];
const from = getStaticSegmentBound(segment == null ? void 0 : segment.from);
const to = getStaticSegmentBound(segment == null ? void 0 : segment.to);
const segmentPath = `${path}.segments[${index}]`;
if ((segment == null ? void 0 : segment.invalidBoundary) === "entity") {
addWarning(diagnostics, "segments.unsupported_entity_boundary", "Entity-backed segment boundaries are not supported; ignoring this segment.", segmentPath, entity);
continue;
}
if ((segment == null ? void 0 : segment.invalidBoundary) === "malformed_percent") {
addWarning(diagnostics, "segments.invalid_percentage", "Malformed percentage segment boundary; ignoring this segment.", segmentPath, entity);
continue;
}
if (segment == null ? void 0 : segment.invalidBoundary) {
addWarning(diagnostics, "segments.invalid_boundary", 'Segment boundary must be a numeric value or a percentage such as "35%"; ignoring this segment.', segmentPath, entity);
continue;
}
if (Number.isFinite(from) && Number.isFinite(to) && from > to) {
addWarning(diagnostics, "segments.from_gt_to", "Segment start is greater than segment end.", segmentPath, entity);
}
if (Number.isFinite(min) && Number.isFinite(max)) {
if (Number.isFinite(from) && (from < min || from > max)) {
addWarning(diagnostics, "segments.outside_scale", "Segment boundary is outside the fixed scale range.", segmentPath, entity);
}
if (Number.isFinite(to) && (to < min || to > max)) {
addWarning(diagnostics, "segments.outside_scale", "Segment boundary is outside the fixed scale range.", segmentPath, entity);
}
}
if (Number.isFinite(from) && Number.isFinite(to)) {
staticSegments.push({ from, to, path: segmentPath });
}
}
const sorted = [...staticSegments].sort((a, b) => a.from - b.from);
for (let index = 1; index < sorted.length; index += 1) {
const previous = sorted[index - 1];
const current = sorted[index];
if (current.from < previous.to) {
addWarning(diagnostics, "segments.overlap", "Fixed segments overlap.", current.path, entity);
}
}
}
function validateGradientStops(diagnostics, stops, path, entity = null) {
var _a;
if (!Array.isArray(stops)) return;
const seenPositions = /* @__PURE__ */ new Set();
for (let index = 0; index < stops.length; index += 1) {
const pos = getFiniteNumber((_a = stops[index]) == null ? void 0 : _a.pos);
const stopPath = `${path}.gradient_stops[${index}]`;
if (Number.isFinite(pos) && (pos < 0 || pos > 100)) {
addWarning(
diagnostics,
"gradient_stops.outside_range",
"Gradient stop position is outside 0..100.",
stopPath,
entity
);
}
if (Number.isFinite(pos)) {
if (seenPositions.has(pos)) {
addWarning(
diagnostics,
"duplicate-gradient-stop-position",
"Multiple gradient stops use the same position value.",
stopPath,
entity
);
} else {
seenPositions.add(pos);
}
}
}
}
function validateConfigScope(diagnostics, config, path, entity = null) {
var _a, _b;
const scaleBounds = validateScaleBounds(diagnostics, config == null ? void 0 : config.scale, path, entity);
validateTargetRange(diagnostics, config, scaleBounds, path, entity);
validateBaselineRange(diagnostics, config, scaleBounds, path, entity);
validateBaselineSuppressesNeedle(diagnostics, config, path, entity);
validateExtremumReset(diagnostics, config, path, entity);
validateMarkerDirections(diagnostics, config, path, entity);
validateBuiltinMarkerLabels(diagnostics, config, path, entity);
validateSegments(diagnostics, (_a = config == null ? void 0 : config.bar) == null ? void 0 : _a.segments, scaleBounds, `${path}.bar`, entity);
validateGradientStops(diagnostics, (_b = config == null ? void 0 : config.bar) == null ? void 0 : _b.gradient_stops, `${path}.bar`, entity);
}
function validateNormalizedConfig(config) {
var _a, _b;
const diagnostics = {
warnings: [],
errors: []
};
if (!config || typeof config !== "object") {
return diagnostics;
}
validateConfigScope(diagnostics, config, "card");
validateGenericMarkers(diagnostics, config.generic_markers, config.generic_markers_invalid, "markers");
const seenEntities = /* @__PURE__ */ new Set();
const entities = Array.isArray(config.entities) ? config.entities : [];
for (let index = 0; index < entities.length; index += 1) {
const entityConfig = entities[index];
const entityId = (_a = entityConfig == null ? void 0 : entityConfig.entity) != null ? _a : null;
const path = `entities[${index}]`;
if (!entityId) {
addWarning(diagnostics, "entities.missing_entity", "Entity row is missing an entity id.", path, null);
continue;
}
if (seenEntities.has(entityId)) {
addWarning(diagnostics, "entities.duplicate_entity", "Duplicate entity id in the same card config.", path, entityId);
} else {
seenEntities.add(entityId);
}
validateConfigScope(diagnostics, entityConfig, path, entityId);
if (Object.prototype.hasOwnProperty.call(entityConfig, "markers")) {
validateGenericMarkers(
diagnostics,
entityConfig.generic_markers,
entityConfig.generic_markers_invalid,
`${path}.markers`,
entityId
);
} else {
const inheritedOverflow = ((_b = entityConfig.generic_markers) != null ? _b : []).filter(
(marker) => {
var _a2, _b2;
return marker.valid && !marker.accepted && ((_b2 = (_a2 = config.generic_markers) == null ? void 0 : _a2[marker.index]) == null ? void 0 : _b2.accepted) === true;
}
);
if (inheritedOverflow.length) {
validateGenericMarkers(diagnostics, inheritedOverflow, false, `${path}.markers`, entityId);
}
}
}
return diagnostics;
}
var init_validate = __esm({
"src/config/validate.js"() {
init_normalize();
}
});
// src/utils/format.js
function formatNumericDisplay(rawVal, decimal = null) {
if (!Number.isFinite(rawVal)) return String(rawVal);
if (decimal !== null) {
return rawVal.toLocaleString(void 0, {
minimumFractionDigits: decimal,
maximumFractionDigits: decimal
});
}
return rawVal.toLocaleString();
}
function isTightUnit(unit) {
return ["h", "m", "s"].includes(String(unit || "").trim());
}
function formatDisplayWithUnit(display, unit) {
if (!unit) return String(display);
const cleanUnit = String(unit);
return `${display}${isTightUnit(cleanUnit) ? "" : " "}${cleanUnit}`;
}
function createNumericPresentation(value, unit, decimal = null) {
const number = formatNumericDisplay(value, decimal);
const cleanUnit = unit ? String(unit) : "";
return {
value,
number,
unit: cleanUnit,
text: formatDisplayWithUnit(number, cleanUnit)
};
}
function createMarkerLabelPresentation(value, unit, precision = null, options = {}) {
const semanticText = typeof options.text === "string" ? options.text : "";
const number = options.showValue === false || value === null || value === void 0 || value === "" ? "" : Number.isFinite(value) ? formatNumericDisplay(value, precision) : String(value);
const cleanUnit = options.showUnit === false ? "" : String(unit != null ? unit : "").trim();
const text = [semanticText, number, cleanUnit].filter(Boolean).join(" ");
return {
value: Number.isFinite(value) ? value : null,
semanticText,
number,
unit: cleanUnit,
showValue: options.showValue !== false,
showUnit: options.showUnit !== false,
text
};
}
function createTextPresentation(text) {
const value = String(text);
return {
value: null,
number: value,
unit: "",
text: value
};
}
var init_format = __esm({
"src/utils/format.js"() {
}
});
// src/view-model/marker-view-model.js
function hasConfiguredSource(source) {
if (!source) return false;
return source.entity !== null && source.entity !== void 0 && source.entity !== "" || source.fixed !== null && source.fixed !== void 0 && source.fixed !== "" || Number.isFinite(source.percent);
}
function normalizeMarkerShape(value, fallback = "circle") {
return MARKER_SHAPES.has(value) ? value : fallback;
}
function createMarkerModel({
id,
type,
value = null,
position = null,
lane,
visible = false,
color = null,
label = null,
labelVisible = false,
shape = "circle",
direction = "inward",
showMarker = true
}) {
return {
id,
type,
value,
position,
lane,
visible: visible === true,
color,
label,
labelVisible: labelVisible === true,
shape: normalizeMarkerShape(shape),
direction,
showMarker: showMarker !== false
};
}
function hasMarkerLabelContent(label) {
return !!(typeof (label == null ? void 0 : label.text) === "string" && label.text.trim()) || (label == null ? void 0 : label.showValue) !== false || (label == null ? void 0 : label.showUnit) !== false;
}
function getMarkerLaneOccupancy(entityConfig) {
var _a;
const targetConfig = entityConfig == null ? void 0 : entityConfig.target_marker;
const peakConfig = entityConfig == null ? void 0 : entityConfig.peak_marker;
const floorConfig = entityConfig == null ? void 0 : entityConfig.floor_marker;
const genericMarkers = ((_a = entityConfig == null ? void 0 : entityConfig.generic_markers) != null ? _a : []).filter((marker) => marker.accepted);
return {
below: (targetConfig == null ? void 0 : targetConfig.enabled) !== false && hasConfiguredSource(targetConfig == null ? void 0 : targetConfig.source) || (floorConfig == null ? void 0 : floorConfig.show) === true || genericMarkers.some((marker) => marker.lane === "below"),
above: (peakConfig == null ? void 0 : peakConfig.show) === true || genericMarkers.some((marker) => marker.lane === "above")
};
}
function getMarkerLabelLaneOccupancy(entityConfig) {
var _a;
const targetConfig = entityConfig == null ? void 0 : entityConfig.target_marker;
const peakConfig = entityConfig == null ? void 0 : entityConfig.peak_marker;
const floorConfig = entityConfig == null ? void 0 : entityConfig.floor_marker;
const genericMarkers = ((_a = entityConfig == null ? void 0 : entityConfig.generic_markers) != null ? _a : []).filter((marker) => marker.accepted);
return {
below: (targetConfig == null ? void 0 : targetConfig.enabled) !== false && hasConfiguredSource(targetConfig == null ? void 0 : targetConfig.source) && (targetConfig == null ? void 0 : targetConfig.show_label) === true && hasMarkerLabelContent({ text: targetConfig.label_text, showValue: targetConfig.label_show_value, showUnit: targetConfig.label_show_unit }) || (floorConfig == null ? void 0 : floorConfig.show) === true && (floorConfig == null ? void 0 : floorConfig.show_label) === true && hasMarkerLabelContent({ text: floorConfig.label_text, showValue: floorConfig.label_show_value, showUnit: floorConfig.label_show_unit }) || genericMarkers.some((marker) => {
var _a2;
return marker.lane === "below" && ((_a2 = marker.label) == null ? void 0 : _a2.show) === true && hasMarkerLabelContent(marker.label);
}),
above: (peakConfig == null ? void 0 : peakConfig.show) === true && (peakConfig == null ? void 0 : peakConfig.show_label) === true && hasMarkerLabelContent({ text: peakConfig.label_text, showValue: peakConfig.label_show_value, showUnit: peakConfig.label_show_unit }) || genericMarkers.some((marker) => {
var _a2;
return marker.lane === "above" && ((_a2 = marker.label) == null ? void 0 : _a2.show) === true && hasMarkerLabelContent(marker.label);
})
};
}
function buildMarkerModels({
entityConfig,
targetValue = null,
targetPosition = null,
targetPresentation = null,
targetLabelPresentation = null,
targetVisible = Number.isFinite(targetPosition),
peakValue = null,
peakPosition = null,
peakPresentation = null,
peakLabelPresentation = null,
peakVisible = Number.isFinite(peakPosition),
floorValue = null,
floorPosition = null,
floorPresentation = null,
floorLabelPresentation = null,
floorVisible = Number.isFinite(floorPosition),
genericMarkers = []
}) {
var _a, _b, _c, _d, _e, _f, _g;
const targetConfig = entityConfig == null ? void 0 : entityConfig.target_marker;
const peakConfig = entityConfig == null ? void 0 : entityConfig.peak_marker;
const floorConfig = entityConfig == null ? void 0 : entityConfig.floor_marker;
const targetEnabled = (targetConfig == null ? void 0 : targetConfig.enabled) !== false;
return [
createMarkerModel({
id: "target",
type: "target",
value: targetValue,
position: targetPosition,
lane: "below",
visible: targetEnabled && targetVisible,
color: (_a = targetConfig == null ? void 0 : targetConfig.color) != null ? _a : null,
label: targetLabelPresentation != null ? targetLabelPresentation : targetPresentation,
labelVisible: targetEnabled && (targetConfig == null ? void 0 : targetConfig.show_label) === true && hasMarkerLabelContent({ text: targetConfig.label_text, showValue: targetConfig.label_show_value, showUnit: targetConfig.label_show_unit }),
shape: (_b = targetConfig == null ? void 0 : targetConfig.shape) != null ? _b : "diamond",
direction: (_c = targetConfig == null ? void 0 : targetConfig.direction) != null ? _c : "inward"
}),
createMarkerModel({
id: "floor",
type: "floor",
value: floorValue,
position: floorPosition,
lane: "below",
visible: (floorConfig == null ? void 0 : floorConfig.show) === true && floorVisible,
color: (_d = floorConfig == null ? void 0 : floorConfig.color) != null ? _d : null,
label: floorLabelPresentation != null ? floorLabelPresentation : floorPresentation,
labelVisible: (floorConfig == null ? void 0 : floorConfig.show) === true && (floorConfig == null ? void 0 : floorConfig.show_label) === true && hasMarkerLabelContent({ text: floorConfig.label_text, showValue: floorConfig.label_show_value, showUnit: floorConfig.label_show_unit }),
shape: "triangle",
direction: (_e = floorConfig == null ? void 0 : floorConfig.direction) != null ? _e : "inward"
}),
createMarkerModel({
id: "peak",
type: "peak",
value: peakValue,
position: peakPosition,
lane: "above",
visible: (peakConfig == null ? void 0 : peakConfig.show) === true && peakVisible,
color: (_f = peakConfig == null ? void 0 : peakConfig.color) != null ? _f : null,
label: peakLabelPresentation != null ? peakLabelPresentation : peakPresentation,
labelVisible: (peakConfig == null ? void 0 : peakConfig.show) === true && (peakConfig == null ? void 0 : peakConfig.show_label) === true && hasMarkerLabelContent({ text: peakConfig.label_text, showValue: peakConfig.label_show_value, showUnit: peakConfig.label_show_unit }),
shape: "triangle",
direction: (_g = peakConfig == null ? void 0 : peakConfig.direction) != null ? _g : "inward"
}),
...genericMarkers.map((marker) => createMarkerModel({
id: marker.id,
type: "generic",
value: marker.value,
position: marker.position,
lane: marker.lane,
visible: marker.visible,
color: marker.color,
label: marker.label,
labelVisible: marker.labelVisible && hasMarkerLabelContent(marker.label),
shape: marker.shape,
direction: marker.direction,
showMarker: marker.showMarker
}))
];
}
var MARKER_SHAPES;
var init_marker_view_model = __esm({
"src/view-model/marker-view-model.js"() {
MARKER_SHAPES = /* @__PURE__ */ new Set(["circle", "diamond", "triangle", "chevron", "arrow", "pin"]);
}
});
// src/view-model/row-view-model.js
function getDefaultEntityIcon(stateObj, entityId = "") {
var _a, _b, _c;
const deviceClass = String((_b = (_a = stateObj == null ? void 0 : stateObj.attributes) == null ? void 0 : _a.device_class) != null ? _b : "").trim();
if (deviceClass) {
const deviceClassIcons = {
apparent_power: "mdi:flash",
battery: "mdi:battery",
carbon_dioxide: "mdi:molecule-co2",
current: "mdi:current-ac",
energy: "mdi:lightning-bolt",
gas: "mdi:meter-gas",
humidity: "mdi:water-percent",
monetary: "mdi:cash",
power: "mdi:flash",
pressure: "mdi:gauge",
temperature: "mdi:thermometer",
voltage: "mdi:sine-wave",
water: "mdi:water",
weight: "mdi:weight",
wind_speed: "mdi:weather-windy"
};
if (deviceClassIcons[deviceClass]) {
return deviceClassIcons[deviceClass];
}
}
const domain = String(entityId || "").split(".")[0];
const domainIcons = {
sensor: "mdi:eye",
binary_sensor: "mdi:radiobox-marked",
switch: "mdi:toggle-switch-variant",
light: "mdi:lightbulb"
};
return (_c = domainIcons[domain]) != null ? _c : null;
}
function toScalePct(value, minValue, maxValue) {
if (!Number.isFinite(value)) return null;
const safeMin = Number.isFinite(minValue) ? minValue : 0;
const safeMax = Number.isFinite(maxValue) ? maxValue : 100;
const range = safeMax - safeMin || 1;
return Math.min(100, Math.max(0, (value - safeMin) / range * 100));
}
function parseColorToRgb(color) {
const value = String(color || "").trim();
if (!value) return null;
const hexMatch = value.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
if (hexMatch) {
const hex = hexMatch[1];
const full = hex.length === 3 ? hex.split("").map((char) => char + char).join("") : hex;
return {
r: parseInt(full.slice(0, 2), 16),
g: parseInt(full.slice(2, 4), 16),
b: parseInt(full.slice(4, 6), 16)
};
}
const rgbMatch = value.match(/^rgba?\(([^)]+)\)$/i);
if (rgbMatch) {
const parts = rgbMatch[1].split(",").map((part) => part.trim());
if (parts.length >= 3) {
return {
r: Math.max(0, Math.min(255, parseFloat(parts[0]))),
g: Math.max(0, Math.min(255, parseFloat(parts[1]))),
b: Math.max(0, Math.min(255, parseFloat(parts[2])))
};
}
}
return null;
}
function getNeedleBorderColor(color) {
const rgb = parseColorToRgb(color);
if (!rgb) return "#000000";
const toLinear = (channel) => {
const srgb = channel / 255;
return srgb <= 0.04045 ? srgb / 12.92 : ((srgb + 0.055) / 1.055) ** 2.4;
};
const luminance = 0.2126 * toLinear(rgb.r) + 0.7152 * toLinear(rgb.g) + 0.0722 * toLinear(rgb.b);
return luminance < 0.22 ? "#ffffff" : "#000000";
}
function getNeedleState(entityConfig, numericValue, minValue, maxValue, baselinePercent) {
var _a, _b, _c;
const needle = (_a = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _a.needle;
if (!(needle == null ? void 0 : needle.show) || Number.isFinite(baselinePercent) || !Number.isFinite(numericValue)) {
const color2 = (_b = needle == null ? void 0 : needle.color) != null ? _b : "#ffffff";
return {
show: false,
percent: null,
pct: null,
color: color2,
borderColor: getNeedleBorderColor(color2),
edge: "middle"
};
}
const color = (_c = needle.color) != null ? _c : "#ffffff";
const percent = Math.min(100, Math.max(0, toScalePct(numericValue, minValue, maxValue)));
return {
show: true,
percent,
pct: percent,
color,
borderColor: getNeedleBorderColor(color),
edge: percent <= 0 ? "left" : percent >= 100 ? "right" : "middle"
};
}
function getExtremumState(numericValue, minValue, maxValue, tracker, direction, enabled) {
if (!enabled) {
return {
value: null,
percent: null,
display: null,
visible: false
};
}
const existingValue = getFiniteNumber(tracker == null ? void 0 : tracker.value);
if (!Number.isFinite(existingValue) && !Number.isFinite(numericValue)) {
return {
value: null,
percent: null,
visible: false
};
}
const value = Number.isFinite(existingValue) ? Number.isFinite(numericValue) ? direction === "min" ? Math.min(existingValue, numericValue) : Math.max(existingValue, numericValue) : existingValue : numericValue;
return {
value,
percent: toScalePct(value, minValue, maxValue),
visible: true
};
}
function buildRowViewModel(options) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha;
const {
hass,
cardConfig,
entityConfig,
entityState,
peaks,
extrema,
previousScale
} = options;
void cardConfig;
const entityId = (_a = entityConfig == null ? void 0 : entityConfig.entity) != null ? _a : null;
const rawState = (_b = entityState == null ? void 0 : entityState.state) != null ? _b : "";
const numericValue = getFiniteNumber(rawState);
const rawUnit = (_d = (_c = entityState == null ? void 0 : entityState.attributes) == null ? void 0 : _c.unit_of_measurement) != null ? _d : "";
const configuredUnit = (_e = entityConfig == null ? void 0 : entityConfig.formatting) == null ? void 0 : _e.unit;
const targetUnit = (_f = configuredUnit != null ? configuredUnit : rawUnit) != null ? _f : "";
const displayUnit = numericValue !== null ? targetUnit : "";
const { min: safeMin, max: safeMax } = getResolvedScale(hass, entityConfig == null ? void 0 : entityConfig.scale, previousScale);
const percent = numericValue !== null ? toScalePct(numericValue, safeMin, safeMax) : 0;
const decimal = (_h = (_g = entityConfig == null ? void 0 : entityConfig.formatting) == null ? void 0 : _g.decimal) != null ? _h : null;
const primaryPresentation = numericValue === null ? createTextPresentation(rawState) : createNumericPresentation(numericValue, displayUnit, decimal);
const targetValue = ((_i = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _i.enabled) === false ? null : getNormalizedResolvableNumericValue(hass, (_j = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _j.source, safeMin, safeMax);
const targetPercent = targetValue !== null ? toScalePct(targetValue, safeMin, safeMax) : null;
const targetVisible = targetValue !== null;
const targetDecimal = (_l = (_k = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _k.label_decimal) != null ? _l : decimal;
const targetPresentation = targetValue !== null ? createNumericPresentation(targetValue, targetUnit, targetDecimal) : null;
const targetLabelPresentation = targetValue !== null ? createMarkerLabelPresentation(targetValue, targetUnit, (_n = (_m = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _m.label_precision) != null ? _n : targetDecimal, {
text: (_o = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _o.label_text,
showValue: (_p = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _p.label_show_value,
showUnit: (_q = entityConfig == null ? void 0 : entityConfig.target_marker) == null ? void 0 : _q.label_show_unit
}) : null;
const baselineValue = ((_r = entityConfig == null ? void 0 : entityConfig.baseline) == null ? void 0 : _r.enabled) === false ? null : getNormalizedResolvableNumericValue(hass, (_s = entityConfig == null ? void 0 : entityConfig.baseline) == null ? void 0 : _s.at, safeMin, safeMax);
const baselinePercent = Number.isFinite(baselineValue) ? toScalePct(baselineValue, safeMin, safeMax) : null;
const baselineVisible = Number.isFinite(baselineValue);
const legacyPeak = Number.isFinite(getFiniteNumber(peaks == null ? void 0 : peaks[entityId])) ? { value: getFiniteNumber(peaks == null ? void 0 : peaks[entityId]) } : null;
const peakState = getExtremumState(
numericValue,
safeMin,
safeMax,
(_t = extrema == null ? void 0 : extrema.peak) != null ? _t : legacyPeak,
"max",
((_u = entityConfig == null ? void 0 : entityConfig.peak_marker) == null ? void 0 : _u.show) === true
);
const floorState = getExtremumState(
numericValue,
safeMin,
safeMax,
extrema == null ? void 0 : extrema.floor,
"min",
((_v = entityConfig == null ? void 0 : entityConfig.floor_marker) == null ? void 0 : _v.show) === true
);
const peakDecimal = (_x = (_w = entityConfig == null ? void 0 : entityConfig.peak_marker) == null ? void 0 : _w.label_decimal) != null ? _x : decimal;
const floorDecimal = (_z = (_y = entityConfig == null ? void 0 : entityConfig.floor_marker) == null ? void 0 : _y.label_decimal) != null ? _z : decimal;
const peakLabelPrecision = (_B = (_A = entityConfig == null ? void 0 : entityConfig.peak_marker) == null ? void 0 : _A.label_precision) != null ? _B : peakDecimal;
const floorLabelPrecision = (_D = (_C = entityConfig == null ? void 0 : entityConfig.floor_marker) == null ? void 0 : _C.label_precision) != null ? _D : floorDecimal;
const peakPresentation = peakState.visible ? createNumericPresentation(peakState.value, targetUnit, peakDecimal) : null;
const floorPresentation = floorState.visible ? createNumericPresentation(floorState.value, targetUnit, floorDecimal) : null;
const peakLabelPresentation = peakState.visible ? createMarkerLabelPresentation(peakState.value, targetUnit, peakLabelPrecision, {
text: (_E = entityConfig == null ? void 0 : entityConfig.peak_marker) == null ? void 0 : _E.label_text,
showValue: (_F = entityConfig == null ? void 0 : entityConfig.peak_marker) == null ? void 0 : _F.label_show_value,
showUnit: (_G = entityConfig == null ? void 0 : entityConfig.peak_marker) == null ? void 0 : _G.label_show_unit
}) : null;
const floorLabelPresentation = floorState.visible ? createMarkerLabelPresentation(floorState.value, targetUnit, floorLabelPrecision, {
text: (_H = entityConfig == null ? void 0 : entityConfig.floor_marker) == null ? void 0 : _H.label_text,
showValue: (_I = entityConfig == null ? void 0 : entityConfig.floor_marker) == null ? void 0 : _I.label_show_value,
showUnit: (_J = entityConfig == null ? void 0 : entityConfig.floor_marker) == null ? void 0 : _J.label_show_unit
}) : null;
const genericMarkers = ((_K = entityConfig == null ? void 0 : entityConfig.generic_markers) != null ? _K : []).filter((marker) => marker.accepted).map((marker) => {
var _a2, _b2, _c2, _d2, _e2;
const value = getNormalizedResolvableNumericValue(hass, marker.source, safeMin, safeMax);
const visible = Number.isFinite(value);
const markerPrecision = (_a2 = marker.label.precision) != null ? _a2 : decimal;
const labelStateObj = marker.label.entity ? (_b2 = hass == null ? void 0 : hass.states) == null ? void 0 : _b2[marker.label.entity] : null;
const rawLabelState = labelStateObj == null ? void 0 : labelStateObj.state;
const cleanLabelState = typeof rawLabelState === "string" ? rawLabelState.trim() : "";
const usableLabelState = labelStateObj && cleanLabelState && !["unknown", "unavailable"].includes(cleanLabelState.toLowerCase());
const labelValue = marker.label.entity ? usableLabelState ? (_c2 = getFiniteNumber(cleanLabelState)) != null ? _c2 : cleanLabelState : null : value;
const labelUnit = marker.label.entity ? usableLabelState ? (_e2 = (_d2 = labelStateObj == null ? void 0 : labelStateObj.attributes) == null ? void 0 : _d2.unit_of_measurement) != null ? _e2 : "" : "" : targetUnit;
const label = marker.label.show ? createMarkerLabelPresentation(labelValue, labelUnit, markerPrecision, {
text: marker.label.text,
showValue: marker.label.showValue,
showUnit: marker.label.showUnit
}) : null;
return {
id: marker.id,
value,
position: visible ? toScalePct(value, safeMin, safeMax) : null,
lane: marker.lane,
visible,
color: marker.color,
shape: marker.shape,
direction: marker.direction,
showMarker: marker.showMarker,
label,
labelVisible: marker.label.show
};
});
const markers = buildMarkerModels({
entityConfig,
targetValue,
targetPosition: targetPercent,
targetPresentation,
targetLabelPresentation,
targetVisible,
peakValue: peakState.value,
peakPosition: peakState.percent,
peakPresentation,
peakLabelPresentation,
peakVisible: peakState.visible,
floorValue: floorState.value,
floorPosition: floorState.percent,
floorPresentation,
floorLabelPresentation,
floorVisible: floorState.visible,
genericMarkers
});
return {
entityId,
name: (_N = (_M = entityConfig == null ? void 0 : entityConfig.name) != null ? _M : (_L = entityState == null ? void 0 : entityState.attributes) == null ? void 0 : _L.friendly_name) != null ? _N : entityId,
icon: (entityConfig == null ? void 0 : entityConfig.icon) === false ? false : (_Q = (_P = entityConfig == null ? void 0 : entityConfig.icon) != null ? _P : (_O = entityState == null ? void 0 : entityState.attributes) == null ? void 0 : _O.icon) != null ? _Q : getDefaultEntityIcon(entityState, entityId),
state: rawState,
numericValue,
rawUnit,
min: safeMin,
max: safeMax,
percent,
displayValue: primaryPresentation.number,
displayUnit: primaryPresentation.unit,
primaryPresentation,
unit: primaryPresentation.unit,
barColor: (_S = (_R = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _R.color) != null ? _S : null,
fillStyle: (_U = (_T = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _T.fill_style) != null ? _U : null,
target: targetValue,
targetPercent,
targetDisplay: (_V = targetPresentation == null ? void 0 : targetPresentation.text) != null ? _V : null,
targetPresentation,
targetLabelPresentation,
targetVisible,
baseline: baselineValue,
baselinePercent,
baselineVisible,
peak: peakState.value,
peakPercent: peakState.percent,
peakDisplay: (_W = peakPresentation == null ? void 0 : peakPresentation.number) != null ? _W : null,
peakPresentation,
peakLabelPresentation,
peakVisible: peakState.visible,
floor: floorState.value,
floorPercent: floorState.percent,
floorDisplay: (_X = floorPresentation == null ? void 0 : floorPresentation.number) != null ? _X : null,
floorPresentation,
floorLabelPresentation,
floorVisible: floorState.visible,
markers,
markerLaneOccupancy: getMarkerLaneOccupancy(entityConfig),
markerLabelLaneOccupancy: getMarkerLabelLaneOccupancy(entityConfig),
segments: (_Z = (_Y = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _Y.segments) != null ? _Z : null,
gradientStops: (_$ = (__ = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : __.gradient_stops) != null ? _$ : null,
needle: getNeedleState(entityConfig, numericValue, safeMin, safeMax, baselinePercent),
classes: {
labelPosition: (_ca = (_ba = (_aa = entityConfig == null ? void 0 : entityConfig.layout) == null ? void 0 : _aa.label) == null ? void 0 : _ba.position) != null ? _ca : "left",
animated: ((_da = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _da.animated) !== false
},
attributes: {
entity: entityId,
baseHeight: (_fa = (_ea = entityConfig == null ? void 0 : entityConfig.layout) == null ? void 0 : _ea.height) != null ? _fa : 38,
heightExplicit: ((_ga = entityConfig == null ? void 0 : entityConfig.layout) == null ? void 0 : _ga.height_explicit) === true,
barAnimated: ((_ha = entityConfig == null ? void 0 : entityConfig.bar) == null ? void 0 : _ha.animated) !== false
}
};
}
var init_row_view_model = __esm({
"src/view-model/row-view-model.js"() {
init_resolve();
init_normalize();
init_format();
init_marker_view_model();
}
});
// src/utils/dom.js
function escapeHtml(value) {
if (value == null) return "";
return String(value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
}
var init_dom = __esm({
"src/utils/dom.js"() {
}
});
// src/card/SensorBarCard.js
function getRevealTransitionDuration(previousGeometry, nextGeometry) {
if (!previousGeometry || !nextGeometry || !Number.isFinite(previousGeometry.valuePercent) || !Number.isFinite(nextGeometry.valuePercent)) {
return 600;
}
const previousBaseline = Number.isFinite(previousGeometry.baselinePercent) ? previousGeometry.baselinePercent : 0;
const nextBaseline = Number.isFinite(nextGeometry.baselinePercent) ? nextGeometry.baselinePercent : 0;
const delta = Math.max(
Math.abs(nextGeometry.valuePercent - previousGeometry.valuePercent),
Math.abs(nextBaseline - previousBaseline)
);
const ordinaryDuration = Math.round(Math.min(600, Math.max(150, 600 - 15 * Math.max(0, delta - 5))));
const crossesBaseline = Number.isFinite(previousGeometry.baselinePercent) && Number.isFinite(nextGeometry.baselinePercent) && (previousGeometry.valuePercent - previousGeometry.baselinePercent) * (nextGeometry.valuePercent - nextGeometry.baselinePercent) < 0;
return crossesBaseline ? Math.min(ordinaryDuration, 300) : ordinaryDuration;
}
var SensorBarCard;
var init_SensorBarCard = __esm({
"src/card/SensorBarCard.js"() {
init_normalize();
init_resolve();
init_validate();
init_row_view_model();
init_marker_view_model();
init_dom();
init_format();
init_extrema();
SensorBarCard = class extends HTMLElement {
static getConfigElement() {
return document.createElement("sensor-bar-card-plus-editor");
}
constructor() {
super();
this.attachShadow({ mode: "open" });
this._baseDomReady = false;
this._config = {};
this._diagnostics = { warnings: [], errors: [] };
this._lastDiagnosticsSignature = null;
this._hass = null;
this._extrema = /* @__PURE__ */ new WeakMap();
this._rowScales = /* @__PURE__ */ new WeakMap();
this._rowGeneration = 0;
this._rowPresence = [];
this._leftModeResponsiveHistory = /* @__PURE__ */ new WeakMap();
this._rendered = false;
this._resizeObserver = null;
this._densityPassScheduled = false;
this._densityPassDirty = false;
this._densityPassFrame = null;
this._densityPassRetries = 0;
this._boundWindowResize = () => this._schedulePostLayoutDensityPass();
this._markerHover = null;
this._boundMarkerPointerOver = (event) => this._handleMarkerPointerOver(event);
this._boundMarkerPointerOut = (event) => this._handleMarkerPointerOut(event);
this._ensureBaseDom();
}
connectedCallback() {
window.addEventListener("resize", this._boundWindowResize, { passive: true });
this._setupResizeObserver();
this._schedulePostLayoutDensityPass();
}
disconnectedCallback() {
this._rowGeneration += 1;
window.removeEventListener("resize", this._boundWindowResize);
this._clearMarkerHover();
this._disconnectResizeObserver();
if (this._densityPassFrame) {
cancelAnimationFrame(this._densityPassFrame);
this._densityPassFrame = null;
}
this._densityPassScheduled = false;
this._densityPassDirty = false;
}
setConfig(config) {
var _a;
if (!config.entities && !config.entity) {
throw new Error("You must define entities or entity");
}
this._rendered = false;
this._rowGeneration += 1;
const previousConfig = this._config;
this._config = this.normalizeCardConfig(config);
this._rowScales = /* @__PURE__ */ new WeakMap();
this._reconcileRowHistory((_a = previousConfig == null ? void 0 : previousConfig.entities) != null ? _a : []);
this._diagnostics = validateNormalizedConfig(this._config);
this._logDiagnostics();
this._render();
}
_reconcileRowHistory(previousRows) {
var _a, _b, _c, _d;
const canonical = (value, key = "") => {
if (Array.isArray(value)) return value.map(canonical);
if (value && typeof value === "object") return Object.fromEntries(Object.keys(value).sort().filter((name) => !["severity", "segment_space", "label_precision_key"].includes(name)).map((name) => [name, canonical(value[name], name)]));
if (key === "fixed") return getNumericValue(null, value);
if (/color/i.test(key) && typeof value === "string" && /^#(?:[\da-f]{3}|[\da-f]{6})$/i.test(value.trim())) {
const hex = value.trim().slice(1).toLowerCase();
return `#${hex.length === 3 ? hex.split("").map((char) => char + char).join("") : hex}`;
}
return value;
};
const signature = (row) => {
var _a2;
return JSON.stringify(canonical({
entity: row.entity,
name: row.name,
icon: (_a2 = row.icon) != null ? _a2 : null,
layout: row.layout,
scale: Object.fromEntries(["min", "max"].map((key) => [key, {
...row.scale[key],
fixed_explicit: row.scale[key].fixed_explicit !== false
}])),
bar: row.bar,
baseline: row.baseline,
formatting: row.formatting,
target_marker: row.target_marker,
peak_marker: row.peak_marker,
floor_marker: row.floor_marker,
generic_markers: row.generic_markers
}));
};
const unmatchedOld = new Set(previousRows);
const matches = /* @__PURE__ */ new Map();
const oldSignatures = new Map(previousRows.map((row) => [row, signature(row)]));
for (const row of this._config.entities) {
const key = signature(row);
const previous = [...unmatchedOld].find((old) => oldSignatures.get(old) === key);
if (!previous) continue;
matches.set(row, previous);
unmatchedOld.delete(previous);
}
const unmatchedNew = this._config.entities.filter((row) => !matches.has(row));
for (const row of unmatchedNew) {
const old = [...unmatchedOld].filter((previous) => previous.entity === row.entity);
const next = unmatchedNew.filter((current) => current.entity === row.entity);
if (old.length !== 1 || next.length !== 1) continue;
matches.set(row, old[0]);
unmatchedOld.delete(old[0]);
}
const extrema = /* @__PURE__ */ new WeakMap();
const responsive = /* @__PURE__ */ new WeakMap();
for (const [row, previous] of matches) {
const stored = this._extrema.get(previous);
const retained = {};
for (const key of ["peak", "floor"]) {
const before = previous[`${key}_marker`];
const after = row[`${key}_marker`];
if ((stored == null ? void 0 : stored[key]) && (before == null ? void 0 : before.show) === true && (after == null ? void 0 : after.show) === true && JSON.stringify(before.reset) === JSON.stringify(after.reset)) {
retained[key] = { ...stored[key] };
}
}
if (retained.peak || retained.floor) extrema.set(row, retained);
if (((_b = (_a = previous.layout) == null ? void 0 : _a.label) == null ? void 0 : _b.position) === "left" && ((_d = (_c = row.layout) == null ? void 0 : _c.label) == null ? void 0 : _d.position) === "left" && this._leftModeResponsiveHistory.has(previous)) {
responsive.set(row, this._leftModeResponsiveHistory.get(previous));
}
}
this._extrema = extrema;
this._leftModeResponsiveHistory = responsive;
}
_logDiagnostics() {
var _a;
const diagnostics = (_a = this._diagnostics) != null ? _a : { warnings: [], errors: [] };
const signature = JSON.stringify(diagnostics);
if (signature === this._lastDiagnosticsSignature) return;
this._lastDiagnosticsSignature = signature;
diagnostics.warnings.forEach((diagnostic) => {
console.warn(`[sensor-bar-card-plus] ${diagnostic.message}`, diagnostic);
});
diagnostics.errors.forEach((diagnostic) => {
console.warn(`[sensor-bar-card-plus] ${diagnostic.message}`, diagnostic);
});
}
// The normalized model is internal only. It preserves today's flat YAML
// while giving future work one structured compatibility layer to build on.
normalizeCardConfig(rawConfig) {
return normalizeCardConfig(rawConfig);
}
normalizeEntityConfig(entityConfig, cardConfig) {
return normalizeEntityConfig(entityConfig, cardConfig);
}
// Internal resolvable shape preserves today's flat `value + *_entity`
// behavior while canonicalizing the normalized form to `fixed + entity`.
normalizeResolvableValue(value, entityValue, percentValue = null) {
return normalizeResolvableValue(value, entityValue, percentValue);
}
_looksLikeEntityId(value) {
return looksLikeEntityId(value);
}
_parsePercentLiteral(value) {
return parsePercentLiteral(value);
}
_getFiniteNumber(value) {
return getFiniteNumber(value);
}
normalizeStructuredResolvableValue(input, inheritedResolvable = null, defaultValue = null, options = {}) {
return normalizeStructuredResolvableValue(input, inheritedResolvable, defaultValue, options);
}
normalizeBaselineDirectionConfig(input, inheritedDirection = null) {
return normalizeBaselineDirectionConfig(input, inheritedDirection);
}
normalizeBaselineConfig(entityConfig, cardConfig) {
return normalizeBaselineConfig(entityConfig, cardConfig);
}
inferSegmentEndValues(segments, fallbackEnd = null) {
const sorted = [...segments].sort((a, b) => a.from - b.from);
return sorted.map((segment, index) => {
var _a;
let to = Number.isFinite(segment.to) ? segment.to : null;
if (!Number.isFinite(to) && index < sorted.length - 1) {
to = sorted[index + 1].from;
}
if (!Number.isFinite(to) && Number.isFinite(fallbackEnd)) {
to = fallbackEnd;
}
return {
from: segment.from,
to,
color: segment.color,
label: (_a = segment.label) != null ? _a : null
};
});
}
normalizeSeverityToSegments(input) {
return normalizeSeverityToSegments(input);
}
_hasResolvableMagnitude(resolvable) {
return !!resolvable && (Number.isFinite(this._getFiniteNumber(resolvable.fixed)) || Number.isFinite(resolvable.percent));
}
normalizeGaugeSegments(input) {
return normalizeGaugeSegments(input);
}
normalizeScaleBound(entityConfig, cardConfig, key, defaultValue) {
return normalizeScaleBound(entityConfig, cardConfig, key, defaultValue);
}
normalizeScaleConfig(entityConfig, cardConfig) {
return normalizeScaleConfig(entityConfig, cardConfig);
}
_fillStyleToColorMode(fillStyle) {
return fillStyleToColorMode(fillStyle);
}
_colorModeToFillStyle(colorMode) {
return colorModeToFillStyle(colorMode);
}
_normalizeBarModeConfig(barConfig = null, flatColorMode = null) {
return normalizeBarModeConfig(barConfig, flatColorMode);
}
_resolveNormalizedBarMode(entityBar, entityConfig, cardBar, cardConfig) {
return resolveNormalizedBarMode(entityBar, entityConfig, cardBar, cardConfig);
}
_normalizeGradientStops(input) {
return normalizeGradientStops(input);
}
normalizeNeedleConfig(input, inheritedNeedle = null) {
return normalizeNeedleConfig(input, inheritedNeedle);
}
normalizeBarConfig(entityConfig, cardConfig) {
return normalizeBarConfig(entityConfig, cardConfig);
}
normalizeLayoutConfig(entityConfig, cardConfig) {
return normalizeLayoutConfig(entityConfig, cardConfig);
}
_clampSupportedRowHeight(height) {
return clampSupportedRowHeight(height);
}
normalizeFormattingConfig(entityConfig, cardConfig) {
return normalizeFormattingConfig(entityConfig, cardConfig);
}
normalizeTargetMarkerConfig(entityConfig, cardConfig) {
return normalizeTargetMarkerConfig(entityConfig, cardConfig);
}
normalizePeakMarkerConfig(entityConfig, cardConfig) {
return normalizePeakMarkerConfig(entityConfig, cardConfig);
}
_normalizeOptionalEnabled(value) {
return normalizeOptionalEnabled(value);
}
set hass(hass) {
const oldHass = this._hass;
this._hass = hass;
if (!this._config.entities) return;
if (!oldHass) {
this._update();
return;
}
if (this._shouldUpdate(oldHass, hass)) {
this._update(oldHass);
}
}
// Merge global config with per-entity overrides
_resolve(entityCfg) {
var _a, _b, _c, _d, _e, _f, _g;
const ecfg = (entityCfg == null ? void 0 : entityCfg._normalized) ? entityCfg : this.normalizeEntityConfig(entityCfg, this._config);
const stateObj = (_c = (_b = (_a = this._hass) == null ? void 0 : _a.states) == null ? void 0 : _b[ecfg.entity]) != null ? _c : null;
return {
...ecfg,
icon: ecfg.icon === false ? false : (_f = (_e = ecfg.icon) != null ? _e : (_d = stateObj == null ? void 0 : stateObj.attributes) == null ? void 0 : _d.icon) != null ? _f : this._getDefaultEntityIcon(stateObj, ecfg.entity),
name: (_g = ecfg.name) != null ? _g : null
};
}
_getStateTimestamp(stateObj) {
var _a;
const rawTimestamp = (_a = stateObj == null ? void 0 : stateObj.last_updated) != null ? _a : stateObj == null ? void 0 : stateObj.last_changed;
const timestamp = rawTimestamp instanceof Date ? rawTimestamp.getTime() : Date.parse(String(rawTimestamp != null ? rawTimestamp : ""));
return Number.isFinite(timestamp) ? timestamp : Date.now();
}
_updateExtrema(entityCfg, normalizedEntity, stateObj) {
var _a, _b, _c;
const sample = getFiniteNumber(stateObj == null ? void 0 : stateObj.state);
if (!Number.isFinite(sample)) return;
const current = (_a = this._extrema.get(entityCfg)) != null ? _a : {};
const timestamp = this._getStateTimestamp(stateObj);
for (const key of ["peak", "floor"]) {
const marker = normalizedEntity == null ? void 0 : normalizedEntity[`${key}_marker`];
if ((marker == null ? void 0 : marker.show) !== true) {
delete current[key];
continue;
}
current[key] = updateExtremum(
(_b = current[key]) != null ? _b : null,
sample,
(_c = marker.reset) != null ? _c : { kind: "never" },
key === "floor" ? "min" : "max",
timestamp
);
}
if (current.peak || current.floor) {
this._extrema.set(entityCfg, current);
} else {
this._extrema.delete(entityCfg);
}
}
_getDefaultEntityIcon(stateObj, entityId = "") {
var _a, _b, _c;
const deviceClass = String((_b = (_a = stateObj == null ? void 0 : stateObj.attributes) == null ? void 0 : _a.device_class) != null ? _b : "").trim();
if (deviceClass) {
const deviceClassIcons = {
apparent_power: "mdi:flash",
battery: "mdi:battery",
carbon_dioxide: "mdi:molecule-co2",
current: "mdi:current-ac",
energy: "mdi:lightning-bolt",
gas: "mdi:meter-gas",
humidity: "mdi:water-percent",
monetary: "mdi:cash",
power: "mdi:flash",
pressure: "mdi:gauge",
temperature: "mdi:thermometer",
voltage: "mdi:sine-wave",
water: "mdi:water",
weight: "mdi:weight",
wind_speed: "mdi:weather-windy"
};
if (deviceClassIcons[deviceClass]) {
return deviceClassIcons[deviceClass];
}
}
const domain = String(entityId || "").split(".")[0];
const domainIcons = {
sensor: "mdi:eye",
binary_sensor: "mdi:radiobox-marked",
switch: "mdi:toggle-switch-variant",
light: "mdi:lightbulb"
};
return (_c = domainIcons[domain]) != null ? _c : null;
}
_shouldUpdate(oldHass, newHass) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
if (!this._config || !this._config.entities) return true;
for (const entityCfg of this._config.entities) {
const ecfg = this._resolve(entityCfg);
const entitiesToWatch = [
entityCfg.entity,
(_b = (_a = ecfg.scale) == null ? void 0 : _a.min) == null ? void 0 : _b.entity,
(_d = (_c = ecfg.scale) == null ? void 0 : _c.max) == null ? void 0 : _d.entity,
(_f = (_e = ecfg.baseline) == null ? void 0 : _e.at) == null ? void 0 : _f.entity,
(_h = (_g = ecfg.target_marker) == null ? void 0 : _g.source) == null ? void 0 : _h.entity,
...((_i = ecfg.generic_markers) != null ? _i : []).filter((marker) => marker.accepted).flatMap((marker) => {
var _a2, _b2;
return [(_a2 = marker.source) == null ? void 0 : _a2.entity, (_b2 = marker.label) == null ? void 0 : _b2.entity];
})
].filter(Boolean);
for (const ent of entitiesToWatch) {
const oldState = (_j = oldHass.states[ent]) != null ? _j : null;
const newState = (_k = newHass.states[ent]) != null ? _k : null;
if (oldState !== newState) {
return true;
}
}
}
return false;
}
_setStyleIfChanged(el, prop, value) {
var _a, _b;
if (!(el == null ? void 0 : el.style)) return false;
const nextValue = value == null ? "" : String(value);
if (prop.startsWith("--")) {
const currentValue2 = typeof el.style.getPropertyValue === "function" ? el.style.getPropertyValue(prop) : (_a = el.style[prop]) != null ? _a : "";
if (currentValue2 === nextValue) return false;
if (typeof el.style.setProperty === "function") {
el.style.setProperty(prop, nextValue);
} else {
el.style[prop] = nextValue;
}
return true;
}
const currentValue = (_b = el.style[prop]) != null ? _b : "";
if (currentValue === nextValue) return false;
el.style[prop] = nextValue;
return true;
}
_setStyleTextIfChanged(el, value) {
var _a;
if (!(el == null ? void 0 : el.style)) return false;
const nextValue = value == null ? "" : String(value);
const currentValue = (_a = el.style.cssText) != null ? _a : "";
if (currentValue === nextValue) return false;
el.style.cssText = nextValue;
return true;
}
_setTextIfChanged(el, value) {
var _a;
if (!el) return false;
const nextValue = value == null ? "" : String(value);
if (((_a = el.textContent) != null ? _a : "") === nextValue) return false;
el.textContent = nextValue;
return true;
}
_setDatasetIfChanged(el, key, value) {
var _a;
if (!(el == null ? void 0 : el.dataset)) return false;
const nextValue = value == null ? "" : String(value);
const currentValue = (_a = el.dataset[key]) != null ? _a : "";
if (currentValue === nextValue) return false;
el.dataset[key] = nextValue;
return true;
}
_setClassNameIfChanged(el, value) {
var _a;
if (!el) return false;
const nextValue = value == null ? "" : String(value);
if (((_a = el.className) != null ? _a : "") === nextValue) return false;
el.className = nextValue;
return true;
}
_repositionAllTargetLabels() {
if (!this.shadowRoot) return;
this.shadowRoot.querySelectorAll(".row[data-entity]").forEach((row) => {
this._positionTargetLabel(row);
this._positionMarkerValueLabel(row, ".peak-value-label", ".peak-marker");
this._positionMarkerValueLabel(row, ".floor-value-label", ".floor-marker");
this._positionGenericMarkerLabels(row);
});
}
_positionGenericMarkerLabels(row) {
var _a, _b;
((_b = (_a = row.querySelectorAll) == null ? void 0 : _a.call(row, ".generic-value-label[data-marker-id]")) != null ? _b : []).forEach((label) => {
const id = label.dataset.markerId;
this._positionMarkerValueLabel(
row,
`.generic-value-label[data-marker-id="${id}"]`,
`.generic-marker[data-marker-id="${id}"]`
);
});
}
_positionTargetLabel(row) {
this._positionMarkerValueLabel(row, ".target-value-label", ".target-marker");
}
_getMarkerLabel(markerEl) {
var _a;
const row = markerEl == null ? void 0 : markerEl.closest(".row");
if (!row) return null;
if (markerEl.matches(".generic-marker[data-marker-id]")) {
const markerId = markerEl.dataset.markerId;
return (_a = [...row.querySelectorAll(".generic-value-label[data-marker-id]")].find((label) => label.dataset.markerId === markerId)) != null ? _a : null;
}
const labelSelector = markerEl.matches(".target-marker") ? ".target-value-label" : markerEl.matches(".peak-marker") ? ".peak-value-label" : markerEl.matches(".floor-marker") ? ".floor-value-label" : null;
return labelSelector ? row.querySelector(labelSelector) : null;
}
_getGenericMarkerForLabel(labelEl) {
var _a, _b;
const row = labelEl == null ? void 0 : labelEl.closest(".row");
const markerId = (_a = labelEl == null ? void 0 : labelEl.dataset) == null ? void 0 : _a.markerId;
if (!row || !markerId) return null;
return (_b = [...row.querySelectorAll(".generic-marker[data-marker-id]")].find((marker) => marker.dataset.markerId === markerId)) != null ? _b : null;
}
_setMarkerHover(markerEl) {
var _a;
const label = this._getMarkerLabel(markerEl);
if (!label || markerEl.style.display === "none" || getComputedStyle(label).visibility !== "visible") {
this._clearMarkerHover(markerEl);
return;
}
if (((_a = this._markerHover) == null ? void 0 : _a.label) === label) return;
this._clearMarkerHover();
label.dataset.markerHovered = "true";
this._markerHover = { marker: markerEl, label };
}
_clearMarkerHover(markerEl = null) {
if (!this._markerHover || markerEl && this._markerHover.marker !== markerEl) return;
delete this._markerHover.label.dataset.markerHovered;
this._markerHover = null;
}
_handleMarkerPointerOver(event) {
var _a, _b, _c;
if (event.pointerType === "touch") return;
const target = (_b = (_a = event.target) == null ? void 0 : _a.closest) == null ? void 0 : _b.call(_a, '.generic-value-label[data-show-marker="false"], .generic-marker, .target-marker, .peak-marker, .floor-marker');
const markerEl = ((_c = target == null ? void 0 : target.matches) == null ? void 0 : _c.call(target, ".generic-value-label")) ? this._getGenericMarkerForLabel(target) : target;
if (markerEl) this._setMarkerHover(markerEl);
}
_handleMarkerPointerOut(event) {
var _a, _b;
if (event.pointerType === "touch") return;
const target = (_b = (_a = event.target) == null ? void 0 : _a.closest) == null ? void 0 : _b.call(_a, '.generic-value-label[data-show-marker="false"], .generic-marker, .target-marker, .peak-marker, .floor-marker');
if (!target || target.contains(event.relatedTarget)) return;
const markerEl = target.matches(".generic-value-label") ? this._getGenericMarkerForLabel(target) : target;
if (!markerEl) return;
this._clearMarkerHover(markerEl);
}
_positionMarkerValueLabel(row, labelSelector, markerSelector) {
const track = row.querySelector(".bar-track");
const label = row.querySelector(labelSelector);
const marker = row.querySelector(markerSelector);
if (!track || !label || !marker) return;
if (marker.style.display === "none" || !label.textContent.trim()) {
this._setStyleIfChanged(label, "visibility", "hidden");
return;
}
const trackRect = track.getBoundingClientRect();
const maxLabelWidth = Math.max(0, Math.floor(trackRect.width - 4));
this._setStyleIfChanged(label, "maxWidth", `${maxLabelWidth}px`);
const labelRect = label.getBoundingClientRect();
const markerPercent = parseFloat(marker.style.left);
if (!Number.isFinite(markerPercent) || trackRect.width <= 0 || labelRect.width <= 0 || maxLabelWidth <= 10) {
this._setStyleIfChanged(label, "visibility", "hidden");
return;
}
const markerX = markerPercent / 100 * trackRect.width;
const halfLabel = labelRect.width / 2;
const clampedX = Math.max(halfLabel, Math.min(trackRect.width - halfLabel, markerX));
this._setStyleIfChanged(label, "left", `${clampedX}px`);
this._setStyleIfChanged(label, "transform", "translateX(-50%)");
this._setStyleIfChanged(label, "visibility", "visible");
}
_getEntityNumericValue(entityId) {
return getEntityNumericValue(this._hass, entityId);
}
_getNumericValue(value, entityId = null) {
return getNumericValue(this._hass, value, entityId);
}
_resolvePercentValue(percent, minValue, maxValue) {
return resolvePercentValue(percent, minValue, maxValue);
}
_getNormalizedResolvableNumericValue(resolvable, minValue = null, maxValue = null) {
return getNormalizedResolvableNumericValue(this._hass, resolvable, minValue, maxValue);
}
_hexToRgb(color) {
if (!color || typeof color !== "string") return null;
const hex = color.replace("#", "").trim();
const full = hex.length === 3 ? hex.split("").map((c) => c + c).join("") : hex;
if (!/^[0-9a-fA-F]{6}$/.test(full)) return null;
return {
r: parseInt(full.slice(0, 2), 16),
g: parseInt(full.slice(2, 4), 16),
b: parseInt(full.slice(4, 6), 16)
};
}
_getSeverityInterpolationStops(ecfg, minValue = 0, maxValue = 100) {
const bands = this._getSegmentsForRendering(ecfg, minValue, maxValue);
const sorted = bands.filter((s) => Number.isFinite(s == null ? void 0 : s.from) && Number.isFinite(s == null ? void 0 : s.to) && (s == null ? void 0 : s.color)).sort((a, b) => a.from - b.from);
if (!sorted.length) return [];
const stops = [];
for (let i = 0; i < sorted.length; i++) {
const band = sorted[i];
const rgb = this._hexToRgb(band.color);
if (!rgb) continue;
let anchor;
if (i === 0) {
anchor = band.from;
} else if (i === sorted.length - 1) {
anchor = band.to;
} else {
anchor = band.from + (band.to - band.from) / 2;
}
if (!stops.length || stops[stops.length - 1].p !== anchor) {
stops.push({ p: anchor, ...rgb });
}
}
return stops;
}
_getSeverityBandGradientCss(ecfg, minValue = 0, maxValue = 100) {
const bands = this._getSegmentsForRendering(ecfg, minValue, maxValue);
const sorted = bands.filter((s) => Number.isFinite(s == null ? void 0 : s.from) && Number.isFinite(s == null ? void 0 : s.to) && (s == null ? void 0 : s.color)).sort((a, b) => a.from - b.from);
if (!sorted.length) return null;
const stops = [];
for (const band of sorted) {
stops.push(`${band.color} ${band.from}%`, `${band.color} ${band.to}%`);
}
return `linear-gradient(to right, ${stops.join(", ")})`;
}
_getSoftBandBlendWidthPct() {
return 1.5;
}
_pushGradientColorStop(stops, pos, color) {
if (!Array.isArray(stops) || !color) return;
const clampedPos = Math.min(100, Math.max(0, pos));
const last = stops[stops.length - 1];
if (last && last.color === color && Math.abs(last.p - clampedPos) < 1e-4) return;
stops.push({ p: clampedPos, color });
}
_getSoftBandGradientStops(ecfg, minValue = 0, maxValue = 100) {
const bands = this._getSegmentsForRendering(ecfg, minValue, maxValue);
const sorted = bands.filter((s) => Number.isFinite(s == null ? void 0 : s.from) && Number.isFinite(s == null ? void 0 : s.to) && (s == null ? void 0 : s.color)).sort((a, b) => a.from - b.from);
if (!sorted.length) return [];
const blendWidth = this._getSoftBandBlendWidthPct();
const blendHalf = blendWidth / 2;
const stops = [];
this._pushGradientColorStop(stops, sorted[0].from, sorted[0].color);
for (let i = 0; i < sorted.length - 1; i++) {
const current = sorted[i];
const next = sorted[i + 1];
const boundary = current.to;
const currentWidth = current.to - current.from;
const nextWidth = next.to - next.from;
const soften = currentWidth >= blendWidth && nextWidth >= blendWidth;
if (soften) {
this._pushGradientColorStop(stops, Math.max(current.from, boundary - blendHalf), current.color);
this._pushGradientColorStop(stops, Math.min(next.to, boundary + blendHalf), next.color);
} else {
this._pushGradientColorStop(stops, boundary, current.color);
this._pushGradientColorStop(stops, boundary, next.color);
}
}
this._pushGradientColorStop(stops, sorted[sorted.length - 1].to, sorted[sorted.length - 1].color);
return stops;
}
_getSoftBandGradientCss(ecfg, minValue = 0, maxValue = 100) {
const stops = this._getSoftBandGradientStops(ecfg, minValue, maxValue);
if (!stops.length) return null;
return `linear-gradient(to right, ${stops.map((stop) => `${stop.color} ${stop.p}%`).join(", ")})`;
}
_resolveSegmentBoundaryPct(boundary, minValue, maxValue) {
if (boundary === null || boundary === void 0) return null;
if (typeof boundary === "object" && !Array.isArray(boundary)) {
const fixed2 = this._getFiniteNumber(boundary.fixed);
if (Number.isFinite(fixed2)) {
return this._toScalePct(fixed2, minValue, maxValue);
}
if (Number.isFinite(boundary.percent)) {
return boundary.percent;
}
return null;
}
const percent = this._parsePercentLiteral(boundary);
if (Number.isFinite(percent)) {
return percent;
}
const fixed = this._getFiniteNumber(boundary);
return Number.isFinite(fixed) ? this._toScalePct(fixed, minValue, maxValue) : null;
}
_getEffectiveFillStyle(ecfg) {
var _a, _b, _c, _d;
return (_d = (_c = (_a = ecfg == null ? void 0 : ecfg.bar) == null ? void 0 : _a.fill_style) != null ? _c : this._colorModeToFillStyle((_b = ecfg == null ? void 0 : ecfg.bar) == null ? void 0 : _b.color_mode)) != null ? _d : "bands";
}
_segmentsNeedBoundaryResolution(segments) {
return Array.isArray(segments) && segments.some((segment) => (segment == null ? void 0 : segment.from) && typeof segment.from === "object" && !Array.isArray(segment.from) || (segment == null ? void 0 : segment.to) && typeof segment.to === "object" && !Array.isArray(segment.to));
}
_getSegmentsForRendering(ecfg, minValue = 0, maxValue = 100) {
var _a, _b;
const safeMin = Number.isFinite(minValue) ? minValue : 0;
const safeMax = Number.isFinite(maxValue) ? maxValue : 100;
const rawSegments = (Array.isArray((_a = ecfg.bar) == null ? void 0 : _a.segments) ? ecfg.bar.segments : []).filter((segment) => !(segment == null ? void 0 : segment.invalidBoundary));
if (((_b = ecfg.bar) == null ? void 0 : _b.segment_space) === "scale" || this._segmentsNeedBoundaryResolution(rawSegments)) {
const resolvedSegments = rawSegments.map((segment) => {
var _a2;
return {
from: this._resolveSegmentBoundaryPct(segment.from, safeMin, safeMax),
to: this._resolveSegmentBoundaryPct(segment.to, safeMin, safeMax),
color: segment.color,
label: (_a2 = segment.label) != null ? _a2 : null
};
}).filter((segment) => Number.isFinite(segment.from) && segment.color);
return this.inferSegmentEndValues(resolvedSegments, 100).filter((segment) => Number.isFinite(segment.from) && Number.isFinite(segment.to) && segment.color);
}
return this.inferSegmentEndValues(rawSegments, 100).filter((segment) => Number.isFinite(segment.from) && Number.isFinite(segment.to) && segment.color);
}
_getColor(pct, ecfg, minValue = 0, maxValue = 100) {
const fillStyle = this._getEffectiveFillStyle(ecfg);
if (fillStyle === "solid") return ecfg.bar.color;
if (fillStyle === "gradient" || fillStyle === "band_gradient" || fillStyle === "soft_bands") {
let stops;
if (fillStyle === "band_gradient") {
stops = this._getSeverityInterpolationStops(ecfg, minValue, maxValue);
} else if (fillStyle === "soft_bands") {
stops = this._getSoftBandGradientStops(ecfg, minValue, maxValue).map((stop) => {
const rgb = this._hexToRgb(stop.color);
return rgb ? { p: stop.p, ...rgb } : null;
}).filter(Boolean);
} else if (ecfg.bar.gradient_stops && ecfg.bar.gradient_stops.length >= 2) {
stops = ecfg.bar.gradient_stops.map((s) => {
const hex = s.color.replace("#", "");
const full = hex.length === 3 ? hex.split("").map((c) => c + c).join("") : hex;
return { p: s.pos, r: parseInt(full.slice(0, 2), 16), g: parseInt(full.slice(2, 4), 16), b: parseInt(full.slice(4, 6), 16) };
});
stops.sort((a, b) => a.p - b.p);
} else {
stops = [
{ p: 0, r: 76, g: 175, b: 80 },
{ p: 50, r: 255, g: 152, b: 0 },
{ p: 100, r: 244, g: 67, b: 54 }
];
}
if (!stops || !stops.length) return ecfg.bar.color;
let lo = stops[0], hi = stops[stops.length - 1];
for (let i = 0; i < stops.length - 1; i++) {
if (pct >= stops[i].p && pct <= stops[i + 1].p) {
lo = stops[i];
hi = stops[i + 1];
break;
}
}
const t = lo.p === hi.p ? 0 : (pct - lo.p) / (hi.p - lo.p);
return `rgb(${Math.round(lo.r + t * (hi.r - lo.r))},${Math.round(lo.g + t * (hi.g - lo.g))},${Math.round(lo.b + t * (hi.b - lo.b))})`;
}
for (const s of this._getSegmentsForRendering(ecfg, minValue, maxValue)) {
if (pct >= s.from && pct <= s.to) return s.color;
}
return ecfg.bar.color;
}
_buildFullScaleGradientStyle(stops) {
if (!Array.isArray(stops) || !stops.length) return null;
const cssStops = stops.map((stop) => {
var _a;
const cssColor = (_a = stop.color) != null ? _a : this._rgbToCss(stop);
return cssColor ? `${cssColor} ${stop.p}%` : null;
}).filter(Boolean);
if (!cssStops.length) return null;
return `background:linear-gradient(to right,${cssStops.join(",")});background-repeat:no-repeat;`;
}
_getGradientInterpolationStops(ecfg, minValue = 0, maxValue = 100) {
const fillStyle = this._getEffectiveFillStyle(ecfg);
if (fillStyle === "band_gradient") {
return this._getSeverityInterpolationStops(ecfg, minValue, maxValue);
}
if (fillStyle === "soft_bands") {
return this._getSoftBandGradientStops(ecfg, minValue, maxValue).map((stop) => {
const rgb = this._hexToRgb(stop.color);
return rgb ? { p: stop.p, ...rgb } : null;
}).filter(Boolean).sort((a, b) => a.p - b.p);
}
if (ecfg.bar.gradient_stops && ecfg.bar.gradient_stops.length >= 2) {
return ecfg.bar.gradient_stops.map((s) => {
const rgb = this._hexToRgb(s.color);
return rgb ? { p: s.pos, ...rgb } : null;
}).filter(Boolean).sort((a, b) => a.p - b.p);
}
return [
{ p: 0, r: 76, g: 175, b: 80 },
{ p: 50, r: 255, g: 152, b: 0 },
{ p: 100, r: 244, g: 67, b: 54 }
];
}
_rgbToCss(rgb) {
if (!rgb) return null;
return `rgb(${rgb.r},${rgb.g},${rgb.b})`;
}
_buildSolidGradientStyle(color) {
return `linear-gradient(to right,${color} 0%,${color} 100%)`;
}
_getBasePaintGradient(color, ecfg, minValue = 0, maxValue = 100) {
var _a;
const fillStyle = this._getEffectiveFillStyle(ecfg);
if (ecfg.bar.solid_fill) {
return this._buildSolidGradientStyle(color);
}
if (fillStyle === "bands") {
return this._getSeverityBandGradientCss(ecfg, minValue, maxValue);
}
if (fillStyle === "soft_bands") {
return this._getSoftBandGradientCss(ecfg, minValue, maxValue);
}
if (fillStyle === "gradient" || fillStyle === "band_gradient") {
const stops = this._getGradientInterpolationStops(ecfg, minValue, maxValue);
return (_a = this._buildFullScaleGradientStyle(stops)) == null ? void 0 : _a.replace(/^background:/, "").replace(/;background-repeat:no-repeat;$/, "");
}
return this._buildSolidGradientStyle(color);
}
_getOverlayGradient(startPct, endPct, color) {
if (!color) return null;
const start = Math.min(100, Math.max(0, startPct));
const end = Math.min(100, Math.max(0, endPct));
if (end <= start) return null;
return `linear-gradient(to right,transparent 0%,transparent ${start}%,${color} ${start}%,${color} ${end}%,transparent ${end}%,transparent 100%)`;
}
_toScalePct(value, minValue, maxValue) {
if (!Number.isFinite(value)) return null;
const safeMin = Number.isFinite(minValue) ? minValue : 0;
const safeMax = Number.isFinite(maxValue) ? maxValue : 100;
const range = safeMax - safeMin || 1;
return Math.min(100, Math.max(0, (value - safeMin) / range * 100));
}
_getRevealTransitionDuration(previousGeometry, nextGeometry) {
return getRevealTransitionDuration(previousGeometry, nextGeometry);
}
_resolveBaselinePct(ecfg, safeMin, safeMax) {
var _a, _b;
if (((_a = ecfg.baseline) == null ? void 0 : _a.enabled) === false) return null;
const baselineValue = this._getNormalizedResolvableNumericValue((_b = ecfg.baseline) == null ? void 0 : _b.at, safeMin, safeMax);
if (!Number.isFinite(baselineValue)) return null;
return this._toScalePct(baselineValue, safeMin, safeMax);
}
_formatNumericDisplay(rawVal, decimal = null) {
return formatNumericDisplay(rawVal, decimal);
}
_getNormalizedPercent(valuePct, baselinePct = null) {
const clampedValue = Math.min(100, Math.max(0, valuePct));
if (!Number.isFinite(baselinePct)) {
return {
usesBaseline: false,
start: 0,
end: clampedValue,
positive: true,
baseline: null,
hidden: clampedValue <= 0
};
}
const clampedBaseline = Math.min(100, Math.max(0, baselinePct));
return {
usesBaseline: true,
start: Math.min(clampedValue, clampedBaseline),
end: Math.max(clampedValue, clampedBaseline),
positive: clampedValue >= clampedBaseline,
baseline: clampedBaseline,
hidden: clampedValue === clampedBaseline
};
}
_getEndpointSemantics(geometry) {
if (geometry == null ? void 0 : geometry.endpointSemantics) {
return geometry.endpointSemantics;
}
if (!(geometry == null ? void 0 : geometry.usesBaseline)) {
return {
left: "scale",
right: "value"
};
}
return geometry.positive ? { left: "baseline", right: "value" } : { left: "value", right: "baseline" };
}
_getRevealCornerRadii(geometry) {
const endpoints = this._getEndpointSemantics(geometry);
const isRounded = (endpointType) => endpointType === "value" || endpointType === "range" || endpointType === "scale";
const leftRadius = isRounded(endpoints.left) ? "6px" : "0";
const rightRadius = isRounded(endpoints.right) ? "6px" : "0";
return `${leftRadius} ${rightRadius} ${rightRadius} ${leftRadius}`;
}
_getAboveTargetOverlayInterval(targetPct = null) {
if (!Number.isFinite(targetPct)) return null;
const start = Math.min(100, Math.max(0, targetPct));
if (start >= 100) return null;
return {
start,
end: 100
};
}
_getAboveTargetLayerGeometry(targetPct = null) {
const interval = this._getAboveTargetOverlayInterval(targetPct);
if (!interval) return null;
return {
start: interval.start,
end: interval.end,
hidden: false
};
}
_getFullScalePaintStyle(ecfg, color, targetPct = null, baselinePct = null, minValue = 0, maxValue = 100) {
var _a, _b, _c, _d, _e, _f;
const layers = [];
const basePaint = this._getBasePaintGradient(color, ecfg, minValue, maxValue);
const clampedBaseline = Number.isFinite(baselinePct) ? Math.min(100, Math.max(0, baselinePct)) : null;
if (Number.isFinite(clampedBaseline)) {
const belowColor = (_c = (_b = (_a = ecfg.baseline) == null ? void 0 : _a.below) == null ? void 0 : _b.color) != null ? _c : null;
const aboveColor = (_f = (_e = (_d = ecfg.baseline) == null ? void 0 : _d.above) == null ? void 0 : _e.color) != null ? _f : null;
const belowOverlay = this._getOverlayGradient(0, clampedBaseline, belowColor);
const aboveOverlay = this._getOverlayGradient(clampedBaseline, 100, aboveColor);
if (belowOverlay) layers.push(belowOverlay);
if (aboveOverlay) layers.push(aboveOverlay);
}
if (basePaint) layers.push(basePaint);
if (!layers.length) return "display:none;";
return `display:block;inset:0;background-image:${layers.join(",")};background-repeat:no-repeat;background-size:100% 100%;`;
}
_getRevealShapeStyle(geometry, h) {
var _a, _b;
const heightValue = typeof h === "number" ? `${h}px` : h;
const start = Math.min(100, Math.max(0, (_a = geometry == null ? void 0 : geometry.start) != null ? _a : 0));
const end = Math.min(100, Math.max(0, (_b = geometry == null ? void 0 : geometry.end) != null ? _b : 0));
if (geometry == null ? void 0 : geometry.hidden) {
return `display:none;height:${heightValue};clip-path:inset(0 100% 0 0 round 0);`;
}
const topInset = "0";
const rightInset = `${Math.max(0, 100 - end)}%`;
const bottomInset = "0";
const leftInset = `${start}%`;
const radii = this._getRevealCornerRadii(geometry);
return `display:block;height:${heightValue};clip-path:inset(${topInset} ${rightInset} ${bottomInset} ${leftInset} round ${radii});`;
}
_getStaticLayerRevealStyle(geometry) {
if (!(geometry == null ? void 0 : geometry.hidden) && Number.isFinite(geometry == null ? void 0 : geometry.start) && Number.isFinite(geometry == null ? void 0 : geometry.end) && geometry.end > geometry.start) {
const start = Math.min(100, Math.max(0, geometry.start));
const end = Math.min(100, Math.max(0, geometry.end));
return `display:block;clip-path:inset(0 ${Math.max(0, 100 - end)}% 0 ${start}% round 0);`;
}
return "display:none;clip-path:inset(0 100% 0 0 round 0);";
}
_getFillPaintLayers(geometry, h, ecfg, color, targetPct = null, baselinePct = null, minValue = 0, maxValue = 100) {
var _a, _b;
const basePaintStyle = this._getFullScalePaintStyle(ecfg, color, targetPct, baselinePct, minValue, maxValue);
const baseLayer = {
id: "base",
zIndex: 1,
visible: true,
paintStyle: basePaintStyle,
revealStyle: "display:block;"
};
const aboveTargetGeometry = this._getAboveTargetLayerGeometry(targetPct);
const aboveTargetLayer = {
id: "above-target",
zIndex: 2,
visible: !!(((_a = ecfg == null ? void 0 : ecfg.bar) == null ? void 0 : _a.above_target_color) && aboveTargetGeometry),
paintStyle: ((_b = ecfg == null ? void 0 : ecfg.bar) == null ? void 0 : _b.above_target_color) ? `display:block;inset:0;background:${ecfg.bar.above_target_color};` : "display:none;",
revealStyle: aboveTargetGeometry ? this._getStaticLayerRevealStyle(aboveTargetGeometry) : this._getStaticLayerRevealStyle({ start: 0, end: 0, hidden: true })
};
return [baseLayer, aboveTargetLayer];
}
_getFillRenderState(pct, h, ecfg, color, targetPct = null, baselinePct = null, minValue = 0, maxValue = 100, needleActive = false) {
var _a, _b;
const geometry = needleActive ? this._getNormalizedPercent(100, null) : this._getNormalizedPercent(pct, baselinePct);
const paintLayers = this._getFillPaintLayers(geometry, h, ecfg, color, targetPct, baselinePct, minValue, maxValue);
return {
geometry,
paintLayers,
paintStyle: (_b = (_a = paintLayers[0]) == null ? void 0 : _a.paintStyle) != null ? _b : "display:none;",
revealStyle: this._getRevealShapeStyle(geometry, h)
};
}
_getNeedleRenderState(rawValue, ecfg, minValue = 0, maxValue = 100, baselinePct = null) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
const needle = (_a = ecfg == null ? void 0 : ecfg.bar) == null ? void 0 : _a.needle;
if (!(needle == null ? void 0 : needle.show)) {
return {
show: false,
pct: null,
color: (_b = needle == null ? void 0 : needle.color) != null ? _b : "#ffffff",
borderColor: this._getNeedleBorderColor((_c = needle == null ? void 0 : needle.color) != null ? _c : "#ffffff"),
edge: "middle"
};
}
if (Number.isFinite(baselinePct)) {
return {
show: false,
pct: null,
color: (_d = needle.color) != null ? _d : "#ffffff",
borderColor: this._getNeedleBorderColor((_e = needle.color) != null ? _e : "#ffffff"),
edge: "middle"
};
}
if (!Number.isFinite(rawValue)) {
return {
show: false,
pct: null,
color: (_f = needle.color) != null ? _f : "#ffffff",
borderColor: this._getNeedleBorderColor((_g = needle.color) != null ? _g : "#ffffff"),
edge: "middle"
};
}
const pct = Math.min(100, Math.max(0, this._toScalePct(rawValue, minValue, maxValue)));
return {
show: true,
pct,
color: (_h = needle.color) != null ? _h : "#ffffff",
borderColor: this._getNeedleBorderColor((_i = needle.color) != null ? _i : "#ffffff"),
edge: pct <= 0 ? "left" : pct >= 100 ? "right" : "middle"
};
}
_ensureBaseDom() {
if (this._baseDomReady) return;
if (this.shadowRoot.querySelector("ha-card")) {
this._baseDomReady = true;
return;
}
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
font-family: 'Segoe UI', system-ui, sans-serif;
position: relative;
z-index: 0;
isolation: isolate;
}
ha-card {
display: block;
background: var(--card-background-color, #fff);
border-radius: 12px;
box-shadow: var(--ha-card-box-shadow, 0 2px 8px rgba(0,0,0,0.08));
overflow: hidden;
padding: 16px;
box-sizing: border-box;
}
.card {
--sbcp-main-gap: 8px;
--sbcp-icon-width: 28px;
--sbcp-above-gap: 10px;
--sbcp-left-label-share: 25%;
--sbcp-value-width: 60px;
--sbcp-bar-min-width: 56px;
--sbcp-target-label-font-size: 12px;
--sbcp-marker-label-lane-size: 15px;
--sbcp-inline-label-padding-x: 8px;
--sbcp-inline-label-padding-y: 2px;
--sbcp-inline-label-font-size: 12px;
min-width: 0;
}
.card[data-compact="compact"] {
--sbcp-main-gap: 6px;
--sbcp-icon-width: 26px;
--sbcp-above-gap: 8px;
--sbcp-left-label-share: 22%;
--sbcp-value-width: 54px;
--sbcp-bar-min-width: 52px;
--sbcp-target-label-font-size: 11px;
--sbcp-inline-label-padding-x: 7px;
--sbcp-inline-label-font-size: 11px;
}
.card[data-compact="tight"] {
--sbcp-main-gap: 5px;
--sbcp-icon-width: 24px;
--sbcp-above-gap: 6px;
--sbcp-left-label-share: 19%;
--sbcp-value-width: 50px;
--sbcp-bar-min-width: 48px;
--sbcp-target-label-font-size: 11px;
--sbcp-inline-label-padding-x: 6px;
--sbcp-inline-label-font-size: 11px;
}
.card[data-compact="dense"] {
--sbcp-main-gap: 4px;
--sbcp-icon-width: 23px;
--sbcp-above-gap: 5px;
--sbcp-left-label-share: 16%;
--sbcp-value-width: 46px;
--sbcp-bar-min-width: 44px;
--sbcp-target-label-font-size: 10px;
--sbcp-inline-label-padding-x: 5px;
--sbcp-inline-label-font-size: 10px;
}
.card[data-compact="compressed"] {
--sbcp-main-gap: 4px;
--sbcp-icon-width: 22px;
--sbcp-above-gap: 4px;
--sbcp-left-label-share: 14%;
--sbcp-value-width: 42px;
--sbcp-bar-min-width: 40px;
--sbcp-target-label-font-size: 10px;
--sbcp-inline-label-padding-x: 5px;
--sbcp-inline-label-font-size: 10px;
}
.card-title {
font-size: 13px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--secondary-text-color, #888);
margin-bottom: 14px;
}
.row {
margin-bottom: 10px;
cursor: pointer;
border-radius: 8px;
padding: 2px 4px;
}
.row:last-child { margin-bottom: 0; }
.row[data-marker-label-lane-below="true"]:not(:last-child) {
margin-bottom: calc(10px + max(0px, var(--sbcp-marker-label-lane-size) - 12px));
}
/* Facing configured lanes overlap by 9px at the approved offsets; 10px leaves a 1px gap. */
.row[data-marker-label-lane-below="true"]:has(+ .row[data-marker-label-lane-above="true"]) {
margin-bottom: calc(10px + max(0px, var(--sbcp-marker-label-lane-size) - 12px) + 10px);
}
.row-stack {
--sbcp-row-height: 38px;
display: flex;
flex-direction: column;
gap: 2px;
}
.row-stack[data-top-value="true"] .main-line.left-mode .value-right {
display: none;
}
.top-right-value {
display: none;
align-self: flex-end;
align-items: center;
justify-content: flex-end;
max-width: 100%;
min-width: 0;
font-size: 13px;
font-weight: 600;
color: var(--primary-text-color, #333);
font-variant-numeric: tabular-nums;
text-align: right;
line-height: 1.1;
margin-bottom: 1px;
}
.top-right-value[data-active="true"] {
display: flex;
}
.above-line,
.hero-line,
.top-right-value {
position: relative;
z-index: 10;
}
.row:hover .bar-track { filter: brightness(0.95); transition: filter 0.15s; }
.main-line {
display: flex;
align-items: center;
gap: var(--sbcp-main-gap);
min-width: 0;
}
.row[data-marker-label-lane-above="true"] .main-line:not(.hero-mode) {
margin-top: var(--sbcp-target-label-font-size);
}
.main-line[data-row-density="tight"] {
gap: calc(var(--sbcp-main-gap) - 1px);
}
.main-line[data-row-density="dense"] {
gap: calc(var(--sbcp-main-gap) - 2px);
}
.main-line[data-row-density="compressed"] {
gap: calc(var(--sbcp-main-gap) - 2px);
}
.main-line:not(.left-mode)[data-row-density="compact"] {
--sbcp-value-width: 52px;
}
.main-line:not(.left-mode)[data-row-density="tight"] {
--sbcp-value-width: 48px;
}
.main-line:not(.left-mode)[data-row-density="dense"] {
--sbcp-value-width: 44px;
}
.main-line:not(.left-mode)[data-row-density="compressed"] {
--sbcp-value-width: 40px;
}
.main-line.off-mode[data-row-density="compressed"] .icon-wrap,
.main-line.above-mode[data-row-density="compressed"] .icon-wrap {
display: none;
}
.main-line.left-mode[data-hide-left-icon="true"] .icon-wrap,
.main-line.above-mode[data-hide-above-icon="true"] .icon-wrap,
.main-line.inside-mode[data-hide-inside-icon="true"] .icon-wrap,
.main-line.inside-mode[data-priority-hide-inside-icon="true"] .icon-wrap,
.main-line.off-mode[data-hide-off-icon="true"] .icon-wrap {
display: none;
}
.main-line.left-mode[data-left-density="normal"] {
--sbcp-left-label-share: 25%;
--sbcp-value-width: 58px;
}
.main-line.left-mode[data-left-density="compact"] {
--sbcp-left-label-share: 22%;
--sbcp-value-width: 53px;
}
.main-line.left-mode[data-left-density="tight"] {
--sbcp-left-label-share: 19%;
--sbcp-value-width: 49px;
}
.main-line.left-mode[data-left-density="dense"] {
--sbcp-left-label-share: 16%;
--sbcp-value-width: 46px;
}
.main-line.left-mode[data-left-density="compressed"] {
--sbcp-left-label-share: 14%;
--sbcp-value-width: 42px;
}
.icon-wrap {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
width: var(--sbcp-icon-width);
height: var(--sbcp-row-height);
min-height: var(--sbcp-row-height);
color: var(--primary-text-color, #333);
line-height: 1;
}
ha-icon {
--mdc-icon-size: 20px;
display: block;
}
.label-left {
position: relative;
z-index: 10;
flex: 1 1 auto;
height: var(--sbcp-row-height);
min-width: 0;
font-size: 13px;
font-weight: 500;
color: var(--primary-text-color, #333);
display: flex;
align-items: center;
}
.label-left[data-hidden="true"],
.label-left[data-priority-hidden="true"] {
display: none;
}
.label-left-text {
display: block;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bar-wrap {
flex: 1 1 var(--sbcp-bar-min-width);
min-width: var(--sbcp-bar-min-width);
position: relative;
}
.bar-track {
position: relative;
width: 100%;
height: var(--sbcp-row-height);
border-radius: 6px;
background: var(--secondary-background-color, #e8e8e8);
overflow: hidden;
}
.bar-fill-reveal {
position: absolute;
inset: 0;
pointer-events: none;
transition: clip-path var(--sbcp-reveal-duration, 600ms) cubic-bezier(0.4,0,0.2,1);
z-index: 1;
}
.bar-paint-layer {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 1;
}
.bar-paint-layer[data-layer="above-target"] {
z-index: 2;
}
.bar-fill-reveal.no-anim {
transition: none;
}
.baseline-indicator {
position: absolute;
top: 0;
bottom: 0;
width: 1px;
transform: translateX(-50%);
background-color: var(--primary-text-color, currentColor);
opacity: 0.6;
pointer-events: none;
transition: left 0.6s cubic-bezier(0.4,0,0.2,1);
z-index: 3;
}
.row[data-bar-animated="false"] .bar-fill-reveal,
.row[data-bar-animated="false"] .needle-marker,
.row[data-bar-animated="false"] .target-marker,
.row[data-bar-animated="false"] .peak-marker,
.row[data-bar-animated="false"] .floor-marker,
.row[data-bar-animated="false"] .generic-marker,
.row[data-bar-animated="false"] .baseline-indicator,
.row[data-bar-animated="false"] .target-value-label,
.row[data-bar-animated="false"] .peak-value-label,
.row[data-bar-animated="false"] .floor-value-label,
.row[data-bar-animated="false"] .generic-value-label {
transition: none;
}
.row[data-bar-animated="false"] .target-value-label {
transition: none;
}
.bar-inner-label {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
padding: 0 6px;
pointer-events: none;
z-index: 10;
}
.bar-inner-label[data-inside-density="compact"] {
gap: 5px;
padding: 0 5px;
}
.bar-inner-label[data-inside-density="tight"] {
gap: 4px;
padding: 0 4px;
}
.bar-inner-label[data-inside-density="dense"] {
gap: 0;
padding: 0 4px;
justify-content: flex-end;
}
.bar-inner-label[data-inside-density="compressed"] {
gap: 0;
padding: 0 4px;
justify-content: flex-end;
}
.bar-inner-label > span {
background: rgba(0,0,0,0.35);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
color: #fff;
font-size: var(--sbcp-inline-label-font-size);
font-weight: 600;
white-space: nowrap;
padding: var(--sbcp-inline-label-padding-y) var(--sbcp-inline-label-padding-x);
border-radius: 20px;
min-width: 0;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
.bar-inner-label .inside-name {
flex: 0 1 auto;
width: fit-content;
max-width: 60%;
display: inline-block;
}
.bar-inner-label[data-inside-density="compact"] .inside-name {
max-width: 56%;
}
.bar-inner-label[data-inside-density="tight"] .inside-name {
max-width: 48%;
}
.bar-inner-label[data-hide-name="true"] .inside-name,
.bar-inner-label[data-priority-hide-name="true"] .inside-name {
display: none;
}
.bar-inner-label .inside-value {
flex: 0 0 auto;
min-width: 0;
max-width: 100%;
display: inline-flex;
align-items: baseline;
}
.bar-inner-label .inside-value[data-hide-value="true"] {
display: none;
}
.bar-inner-label[data-value-fit="compact"] {
padding: 0 2px;
}
.bar-inner-label .inside-value[data-value-fit="compact"] {
padding-left: 2px;
padding-right: 2px;
}
.main-line.inside-mode[data-hide-inside-icon="true"] .bar-inner-label .inside-value,
.main-line.inside-mode[data-priority-hide-inside-icon="true"] .bar-inner-label .inside-value,
.bar-inner-label[data-hide-name="true"] .inside-value,
.bar-inner-label[data-priority-hide-name="true"] .inside-value {
max-width: 100%;
}
.bar-inner-label[data-inside-density="dense"] .inside-value,
.bar-inner-label[data-inside-density="compressed"] .inside-value {
max-width: 100%;
}
.bar-inner-label .inside-value-text {
display: inline-flex;
align-items: baseline;
gap: 0;
max-width: 100%;
min-width: 0;
overflow: hidden;
white-space: nowrap;
background: transparent;
padding: 0;
border-radius: 0;
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
.bar-inner-label .inside-value-text.has-unit {
gap: 2px;
}
.bar-inner-label .inside-value-text.tight-unit {
gap: 0;
}
.bar-inner-label .inside-number {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
background: transparent;
padding: 0;
border-radius: 0;
}
.bar-inner-label .inside-unit {
flex: 0 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: clip;
white-space: nowrap;
font-size: 11px;
font-weight: 400;
color: rgba(255, 255, 255, 0.72);
background: transparent;
padding: 0;
border-radius: 0;
}
.target-value-label {
position: absolute;
top: 100%;
margin-top: 2px;
font-size: var(--sbcp-target-label-font-size);
line-height: 1;
color: var(--marker-color, var(--secondary-text-color, #888));
text-shadow:
0 0 0.6px var(--marker-contrast-color),
0 0 1.2px color-mix(in srgb, var(--marker-contrast-color) 78%, transparent);
background-color: var(--card-background-color, #fff);
padding: 0 2px;
border: 0;
border-radius: 2px;
box-shadow: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
box-sizing: border-box;
pointer-events: none;
z-index: 8;
visibility: hidden;
transition: left 0.6s cubic-bezier(0.4,0,0.2,1);
}
.peak-value-label,
.floor-value-label,
.generic-value-label {
position: absolute;
font-size: var(--sbcp-target-label-font-size);
line-height: 1;
color: var(--marker-color, var(--secondary-text-color, #888));
text-shadow:
0 0 0.6px var(--marker-contrast-color),
0 0 1.2px color-mix(in srgb, var(--marker-contrast-color) 78%, transparent);
background-color: var(--card-background-color, #fff);
padding: 0 2px;
border: 0;
border-radius: 2px;
box-shadow: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
box-sizing: border-box;
pointer-events: none;
z-index: 8;
visibility: hidden;
transition: left 0.6s cubic-bezier(0.4,0,0.2,1);
}
.generic-value-label[data-show-marker="false"] {
pointer-events: auto;
cursor: pointer;
}
.peak-value-label {
bottom: 100%;
margin-bottom: 1px;
}
.floor-value-label {
top: 100%;
margin-top: 2px;
}
.generic-value-label[data-lane="above"] {
bottom: 100%;
margin-bottom: 1px;
}
.generic-value-label[data-lane="below"] {
top: 100%;
margin-top: 2px;
}
.target-value-label[data-marker-hovered="true"],
.peak-value-label[data-marker-hovered="true"],
.floor-value-label[data-marker-hovered="true"],
.generic-value-label[data-marker-hovered="true"] {
z-index: 11;
}
.above-line {
display: grid;
grid-template-columns: var(--sbcp-icon-width) minmax(0, 1fr);
column-gap: var(--sbcp-main-gap);
min-width: 0;
align-items: flex-end;
}
.above-line[data-hide-above-icon="true"],
.above-line[data-above-density="compressed"] {
grid-template-columns: minmax(0, 1fr);
}
.above-bar-label[data-hide-name="true"] .above-bar-label-name,
.above-bar-label[data-priority-hide-name="true"] .above-bar-label-name,
.above-line[data-above-density="compressed"] .above-icon-spacer {
display: none;
}
.above-line[data-hide-above-icon="true"] .above-icon-spacer {
display: none;
}
.above-icon-spacer {
width: var(--sbcp-icon-width);
min-width: 0;
}
.above-bar-label {
flex: 1;
min-width: 0;
display: flex;
justify-content: flex-start;
align-items: center;
gap: var(--sbcp-main-gap);
margin-bottom: 2px;
min-height: 16px;
}
.above-bar-label[data-hide-name="true"],
.above-bar-label[data-priority-hide-name="true"] {
gap: 0;
}
.above-bar-label-name {
flex: 1 1 auto;
min-width: 0;
font-size: 13px;
font-weight: 500;
color: var(--primary-text-color, #333);
line-height: 1.15;
}
.above-bar-label-value {
flex: 0 0 auto;
margin-left: auto;
display: inline-flex;
align-items: baseline;
justify-content: flex-end;
text-align: right;
min-width: 0;
max-width: 100%;
overflow: hidden;
font-size: 13px;
font-weight: 600;
color: var(--primary-text-color, #333);
font-variant-numeric: tabular-nums;
}
.hero-line {
--sbcp-hero-min-value-size: 10px;
--sbcp-hero-base-size: 84px;
--sbcp-hero-compact-size: clamp(50px, calc(var(--sbcp-hero-base-size) * 0.89), 100px);
--sbcp-hero-tight-size: clamp(42px, calc(var(--sbcp-hero-base-size) * 0.75), 84px);
--sbcp-hero-dense-size: clamp(29px, calc(var(--sbcp-hero-base-size) * 0.52), 58px);
--sbcp-hero-compressed-size: clamp(21px, calc(var(--sbcp-hero-base-size) * 0.38), 43px);
--sbcp-hero-xs-size: clamp(15px, calc(var(--sbcp-hero-base-size) * 0.26), 29px);
--sbcp-hero-fit-tight-size: clamp(15px, calc(var(--sbcp-hero-base-size) * 0.26), 29px);
--sbcp-hero-fit-minimum-size: 12px;
min-width: 0;
margin-bottom: 0;
}
.hero-line[data-hero-size="small"] {
--sbcp-hero-base-size: 56px;
}
.hero-line[data-hero-size="large"] {
--sbcp-hero-base-size: 112px;
}
.hero-header {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, auto);
align-items: baseline;
column-gap: var(--sbcp-main-gap);
min-width: 0;
overflow: hidden;
margin-bottom: clamp(2px, calc(var(--sbcp-row-height) * 0.08), 4px);
}
.row[data-marker-label-lane-above="false"] .hero-header {
margin-bottom: 0;
}
.hero-header[data-hide-name="true"] .hero-label,
.hero-header[data-priority-hide-name="true"] .hero-label {
display: none;
}
.hero-header[data-hide-name="true"],
.hero-header[data-priority-hide-name="true"] {
grid-template-columns: minmax(0, 1fr);
}
.hero-header[data-hide-name="true"] .hero-value,
.hero-header[data-priority-hide-name="true"] .hero-value {
grid-column: 1 / -1;
justify-self: stretch;
width: 100%;
}
.hero-label {
min-width: 0;
font-size: 13px;
font-weight: 500;
color: var(--primary-text-color, #333);
line-height: 1.15;
}
.hero-value {
min-width: 0;
max-width: 100%;
display: inline-flex;
align-items: baseline;
justify-content: flex-end;
justify-self: end;
overflow: hidden;
font-size: var(--sbcp-hero-base-size);
font-weight: 700;
color: var(--primary-text-color, #333);
font-variant-numeric: tabular-nums;
line-height: 0.95;
text-align: right;
}
.hero-line[data-hero-density="compact"] .hero-value {
font-size: var(--sbcp-hero-compact-size);
}
.hero-line[data-hero-density="tight"] .hero-value {
font-size: var(--sbcp-hero-tight-size);
}
.hero-line[data-hero-density="dense"] .hero-value {
font-size: var(--sbcp-hero-dense-size);
}
.hero-line[data-hero-density="compressed"] .hero-value {
font-size: var(--sbcp-hero-compressed-size);
}
.hero-line[data-hero-density="xs"] .hero-value {
font-size: var(--sbcp-hero-xs-size);
}
.hero-line[data-hero-value-fit="tight"] .hero-value {
font-size: var(--sbcp-hero-fit-tight-size);
}
.hero-line[data-hero-value-fit="minimum"] .hero-value {
font-size: var(--sbcp-hero-fit-minimum-size);
}
.hero-line[data-hero-value-fit="hidden"] .hero-value {
display: none;
}
.hero-line[data-hide-hero-unit="true"] .hero-value .unit-group {
display: none;
}
.hero-value .value-right-text {
display: inline-flex;
flex: 1 1 auto;
justify-content: flex-end;
gap: 4px;
align-items: baseline;
width: 100%;
min-width: 0;
max-width: 100%;
overflow: hidden;
text-overflow: clip;
white-space: nowrap;
}
.hero-value .value-right-text.tight-unit {
gap: 2px;
}
.hero-value .value-right-number {
flex: 0 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: clip;
white-space: nowrap;
line-height: 0.95;
}
.hero-value .unit-group {
flex: 0 0 auto;
align-self: baseline;
line-height: 1;
overflow: visible;
text-overflow: clip;
}
.hero-value .unit {
font-size: clamp(10px, 0.42em, 16px);
font-weight: 500;
color: var(--secondary-text-color, #888);
line-height: 1;
overflow: visible;
text-overflow: clip;
}
/* \u2500\u2500 Shared marker base \u2500\u2500 */
.peak-marker, .target-marker, .floor-marker, .generic-marker {
position: absolute;
top: 0;
bottom: 0;
width: 0;
transform: translateX(-50%);
pointer-events: none;
transition: left 0.6s cubic-bezier(0.4,0,0.2,1);
--marker-color: #888;
--marker-contrast-color: #f3f4f6;
}
.peak-marker .peak-inset,
.peak-marker .peak-outset,
.target-marker .target-inset,
.target-marker .target-outset,
.floor-marker .floor-inset,
.floor-marker .floor-outset,
.generic-marker .peak-inset,
.generic-marker .peak-outset,
.generic-marker .target-inset,
.generic-marker .target-outset {
pointer-events: auto;
}
.peak-marker .peak-inset:hover,
.peak-marker .peak-outset:hover,
.target-marker .target-inset:hover,
.target-marker .target-outset:hover,
.floor-marker .floor-inset:hover,
.floor-marker .floor-outset:hover,
.generic-marker .peak-inset:hover,
.generic-marker .peak-outset:hover,
.generic-marker .target-inset:hover,
.generic-marker .target-outset:hover,
.marker-shape-svg path[data-shape]:hover {
cursor: none;
}
.target-marker {
z-index: 6;
}
.floor-marker {
z-index: 6;
}
.generic-marker[data-lane="below"] {
z-index: 6;
}
.peak-marker {
z-index: 7;
}
.generic-marker[data-lane="above"] {
z-index: 7;
}
.needle-layer {
position: absolute;
inset: 0;
overflow: hidden;
border-radius: inherit;
pointer-events: none;
z-index: 5;
}
.needle-marker {
position: absolute;
top: 0;
bottom: 0;
width: 7px;
transform: translateX(-50%);
pointer-events: none;
transition: left 0.6s cubic-bezier(0.4,0,0.2,1);
background: linear-gradient(
to right,
var(--needle-border-color, #000000) 0 1px,
var(--needle-color, #ffffff) 1px 6px,
var(--needle-border-color, #000000) 6px 7px
);
border-radius: 0;
box-shadow:
0 0 3px var(--needle-color, #ffffff),
0 0 6px var(--needle-color, #ffffff);
}
.needle-layer .needle-marker[data-edge="right"] {
transform: translateX(-100%);
}
.peak-marker .peak-inset,
.peak-marker .peak-outset,
.target-marker .target-inset,
.target-marker .target-outset,
.floor-marker .floor-inset,
.floor-marker .floor-outset,
.generic-marker[data-lane="above"] .peak-inset,
.generic-marker[data-lane="above"] .peak-outset,
.generic-marker[data-lane="below"] .target-inset,
.generic-marker[data-lane="below"] .target-outset {
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 0;
}
/* Peak marker: large triangle intrudes into the bar, small one sits just above it. */
.peak-marker .peak-inset,
.generic-marker[data-lane="above"] .peak-inset {
top: 0;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 11px solid var(--marker-color);
z-index: 2;
filter:
drop-shadow(0 0 1.2px var(--marker-contrast-color))
drop-shadow(0 0 3px color-mix(in srgb, var(--marker-contrast-color) 78%, transparent));
}
.peak-marker[data-direction="outward"] .peak-inset,
.generic-marker[data-lane="above"][data-direction="outward"] .peak-inset {
border-top-width: 0;
border-top-color: transparent;
border-bottom: 11px solid var(--marker-color);
}
.peak-marker .peak-outset,
.generic-marker[data-lane="above"] .peak-outset {
top: -4px;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 4px solid var(--marker-color);
z-index: 3;
}
/* Target marker: large triangle intrudes into the bar, small one sits just below it. */
.target-marker .target-inset,
.generic-marker[data-lane="below"] .target-inset {
bottom: 0;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 11px solid var(--marker-color);
z-index: 2;
filter:
drop-shadow(0 0 1.2px var(--marker-contrast-color))
drop-shadow(0 0 3px color-mix(in srgb, var(--marker-contrast-color) 78%, transparent));
}
.target-marker .target-outset,
.generic-marker[data-lane="below"] .target-outset {
bottom: -4px;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 4px solid var(--marker-color);
z-index: 3;
}
.floor-marker .floor-inset {
bottom: 0;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 11px solid var(--marker-color);
z-index: 2;
filter:
drop-shadow(0 0 1.2px var(--marker-contrast-color))
drop-shadow(0 0 3px color-mix(in srgb, var(--marker-contrast-color) 78%, transparent));
}
.target-marker[data-direction="outward"] .target-inset,
.floor-marker[data-direction="outward"] .floor-inset,
.generic-marker[data-lane="below"][data-direction="outward"] .target-inset {
border-bottom-width: 0;
border-bottom-color: transparent;
border-top: 11px solid var(--marker-color);
}
.floor-marker .floor-outset {
bottom: -4px;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 4px solid var(--marker-color);
z-index: 3;
}
/* Shared non-triangle marker shapes. Triangle keeps the original CSS geometry. */
.marker-shape-svg {
display: none;
position: absolute;
left: 50%;
width: 16px;
height: 16px;
overflow: visible;
color: var(--marker-color);
pointer-events: none;
z-index: 2;
transform: translateX(-50%);
filter:
drop-shadow(0 0 1.2px var(--marker-contrast-color))
drop-shadow(0 0 3px color-mix(in srgb, var(--marker-contrast-color) 78%, transparent));
}
.peak-marker .marker-shape-svg {
top: 0;
}
.target-marker .marker-shape-svg {
bottom: 0;
}
.floor-marker .marker-shape-svg {
bottom: 0;
}
.generic-marker[data-lane="above"] .marker-shape-svg {
top: 0;
}
.generic-marker[data-lane="below"] .marker-shape-svg {
bottom: 0;
}
.marker-shape-svg[data-lane="above"] {
transform-origin: 50% 0;
}
.marker-shape-svg[data-lane="below"] {
transform-origin: 50% 100%;
}
.marker-shape-svg[data-shape="diamond"],
.marker-shape-svg[data-shape="arrow"],
.marker-shape-svg[data-shape="chevron"],
.marker-shape-svg[data-shape="pin"] {
transform: translateX(-50%) scale(0.75);
}
.marker-shape-svg[data-shape="circle"] {
transform: translateX(-50%) scale(0.64);
}
.peak-marker[data-shape]:not([data-shape="triangle"]) .peak-inset,
.peak-marker[data-shape]:not([data-shape="triangle"]) .peak-outset,
.target-marker[data-shape]:not([data-shape="triangle"]) .target-inset,
.target-marker[data-shape]:not([data-shape="triangle"]) .target-outset,
.floor-marker[data-shape]:not([data-shape="triangle"]) .floor-inset,
.floor-marker[data-shape]:not([data-shape="triangle"]) .floor-outset,
.generic-marker[data-shape]:not([data-shape="triangle"]) .peak-inset,
.generic-marker[data-shape]:not([data-shape="triangle"]) .peak-outset,
.generic-marker[data-shape]:not([data-shape="triangle"]) .target-inset,
.generic-marker[data-shape]:not([data-shape="triangle"]) .target-outset {
display: none;
}
.peak-marker[data-shape]:not([data-shape="triangle"]) .marker-shape-svg,
.target-marker[data-shape]:not([data-shape="triangle"]) .marker-shape-svg {
display: block;
}
.floor-marker[data-shape]:not([data-shape="triangle"]) .marker-shape-svg {
display: block;
}
.generic-marker[data-shape]:not([data-shape="triangle"]) .marker-shape-svg {
display: block;
}
.generic-marker[data-show-marker="false"][data-shape] .peak-inset,
.generic-marker[data-show-marker="false"][data-shape] .peak-outset,
.generic-marker[data-show-marker="false"][data-shape] .target-inset,
.generic-marker[data-show-marker="false"][data-shape] .target-outset,
.generic-marker[data-show-marker="false"][data-shape] .marker-shape-svg {
display: none;
}
.marker-shape-svg path {
display: none;
fill: currentColor;
}
.marker-shape-svg path[data-shape] {
pointer-events: visiblePainted;
}
.marker-shape-svg[data-shape="circle"] path[data-shape="circle"],
.marker-shape-svg[data-shape="diamond"] path[data-shape="diamond"],
.marker-shape-svg[data-shape="chevron"] path[data-shape="chevron"],
.marker-shape-svg[data-shape="arrow"] path[data-shape="arrow"],
.marker-shape-svg[data-shape="pin"] path[data-shape="pin"] {
display: block;
}
.marker-shape-svg[data-shape="chevron"] path[data-shape="chevron"] {
fill: none;
}
.marker-shape-svg[data-shape="chevron"][data-lane="below"][data-direction="inward"] .marker-shape-paths,
.marker-shape-svg[data-shape="arrow"][data-lane="below"][data-direction="inward"] .marker-shape-paths,
.marker-shape-svg[data-shape="pin"][data-lane="below"][data-direction="inward"] .marker-shape-paths,
.marker-shape-svg[data-shape="chevron"][data-lane="above"][data-direction="outward"] .marker-shape-paths,
.marker-shape-svg[data-shape="arrow"][data-lane="above"][data-direction="outward"] .marker-shape-paths,
.marker-shape-svg[data-shape="pin"][data-lane="above"][data-direction="outward"] .marker-shape-paths {
transform-box: view-box;
transform-origin: 0 0;
transform: translateY(16px) scaleY(-1);
}
.value-right {
position: relative;
z-index: 10;
--sbcp-value-extra-width: 0px;
flex: 0 0 calc(var(--sbcp-value-width) + var(--sbcp-value-extra-width));
width: calc(var(--sbcp-value-width) + var(--sbcp-value-extra-width));
min-width: calc(var(--sbcp-value-width) + var(--sbcp-value-extra-width));
max-width: calc(var(--sbcp-value-width) + var(--sbcp-value-extra-width));
height: var(--sbcp-row-height);
text-align: right;
font-size: 13px;
font-weight: 600;
color: var(--primary-text-color, #333);
font-variant-numeric: tabular-nums;
display: flex;
align-items: center;
justify-content: flex-end;
overflow: hidden;
box-sizing: border-box;
padding-right: 1px;
min-width: 0;
}
.value-right-text {
display: inline-flex;
align-items: baseline;
justify-content: flex-end;
gap: 0;
width: 100%;
max-width: 100%;
min-width: 0;
overflow: hidden;
white-space: nowrap;
}
.main-line.off-mode .value-right {
flex-shrink: 1;
}
.value-right-text.has-unit {
gap: 2px;
}
.value-right-text.tight-unit {
gap: 0;
}
.value-right-number {
flex: 0 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 1.1;
}
.value-right .unit-group,
.top-right-value .unit-group,
.above-bar-label-value .unit-group {
flex: 0 1 auto;
display: inline-flex;
align-items: baseline;
min-width: 0;
overflow: hidden;
white-space: nowrap;
line-height: 1.1;
}
.value-right .unit,
.top-right-value .unit,
.above-bar-label-value .unit {
flex: 0 1 auto;
min-width: 0;
display: inline-block;
overflow: hidden;
text-overflow: clip;
white-space: nowrap;
font-size: 11px;
font-weight: 400;
color: var(--secondary-text-color, #888);
line-height: 1.1;
}
.measure-layer {
position: fixed;
left: -9999px;
top: -9999px;
visibility: hidden;
pointer-events: none;
white-space: nowrap;
}
</style>
<ha-card>
<div class="card">
<div class="card-title" style="display:none;"></div>
<div class="rows"></div>
<div class="measure-layer"></div>
</div>
</ha-card>
`;
this.shadowRoot.addEventListener("pointerover", this._boundMarkerPointerOver);
this.shadowRoot.addEventListener("pointerout", this._boundMarkerPointerOut);
this._baseDomReady = true;
}
_render() {
const cfg = this._config;
this._ensureBaseDom();
const titleEl = this.shadowRoot.querySelector(".card-title");
if (titleEl) {
if (cfg.title) {
titleEl.textContent = cfg.title;
titleEl.style.display = "";
} else {
titleEl.textContent = "";
titleEl.style.display = "none";
}
}
this._setupResizeObserver();
this._update();
this._schedulePostLayoutDensityPass();
}
_setupResizeObserver() {
if (!this.isConnected || this._resizeObserver) return;
const surface = this.shadowRoot.querySelector("ha-card");
const card = this.shadowRoot.querySelector(".card");
if (!surface || !card) return;
const observer = new ResizeObserver(() => {
if (!this.isConnected || this._resizeObserver !== observer) return;
this._applyCompactTier();
this._schedulePostLayoutDensityPass();
});
this._resizeObserver = observer;
this._applyCompactTier();
observer.observe(surface);
observer.observe(this);
}
_disconnectResizeObserver() {
if (!this._resizeObserver) return;
this._resizeObserver.disconnect();
this._resizeObserver = null;
}
_isReliableWidth(width, minWidth = 16) {
return Number.isFinite(width) && width >= minWidth;
}
_getHeroLabelReservedWidth(headerEl, labelEl, labelHidden) {
var _a, _b, _c;
if (labelHidden || !headerEl || !labelEl) return 0;
const headerWidth = Math.floor((_b = (_a = headerEl.getBoundingClientRect) == null ? void 0 : _a.call(headerEl).width) != null ? _b : 0);
const naturalLabelWidth = Math.ceil(labelEl.scrollWidth || ((_c = labelEl.getBoundingClientRect) == null ? void 0 : _c.call(labelEl).width) || 0);
if (!this._isReliableWidth(headerWidth, 8) || !Number.isFinite(naturalLabelWidth) || naturalLabelWidth <= 0) {
return 0;
}
return Math.min(naturalLabelWidth, headerWidth * 0.45);
}
_classifyCompactTier(width, currentTier = "normal") {
if (!this._isReliableWidth(width)) return currentTier || "normal";
if (width < 180) return "compressed";
if (width < 220) return "dense";
if (width < 280) return "tight";
if (width < 360) return "compact";
return "normal";
}
_classifyLeftDensity(width, currentDensity = "normal", naturalLabelWidth = Number.POSITIVE_INFINITY) {
if (!this._isReliableWidth(width)) return currentDensity || "normal";
const densities = ["normal", "compact", "tight", "dense", "compressed"];
let density = "normal";
if (width < 170) density = "compressed";
else if (width < 210) density = "dense";
else if (width < 255) density = "tight";
else if (width < 320) density = "compact";
const currentIndex = densities.indexOf(density);
const relaxBy = Number.isFinite(naturalLabelWidth) ? naturalLabelWidth <= 44 && width >= 205 ? 2 : naturalLabelWidth <= 72 && width >= 185 ? 1 : 0 : 0;
return densities[Math.max(0, currentIndex - relaxBy)];
}
_classifyRowDensity(width, currentDensity = "normal") {
if (!this._isReliableWidth(width)) return currentDensity || "normal";
if (width < 150) return "compressed";
if (width < 190) return "dense";
if (width < 245) return "tight";
if (width < 300) return "compact";
return "normal";
}
_schedulePostLayoutDensityPass() {
if (!this.isConnected) return;
if (this._densityPassScheduled) {
this._densityPassDirty = true;
return;
}
this._densityPassScheduled = true;
this._densityPassFrame = requestAnimationFrame(() => {
var _a, _b;
this._densityPassScheduled = false;
this._densityPassFrame = null;
const runAgain = this._densityPassDirty;
this._densityPassDirty = false;
if (!this.isConnected) return;
const surface = (_a = this.shadowRoot) == null ? void 0 : _a.querySelector("ha-card");
const width = (_b = surface == null ? void 0 : surface.getBoundingClientRect().width) != null ? _b : 0;
if (!this._isReliableWidth(width)) {
if (this._densityPassRetries < 4) {
this._densityPassRetries += 1;
this._schedulePostLayoutDensityPass();
}
return;
}
this._densityPassRetries = 0;
this._applyCompactTier();
this._runPostLayoutPasses();
if (runAgain) {
this._schedulePostLayoutDensityPass();
}
});
}
_applyCompactTier() {
if (!this.shadowRoot) return;
const surface = this.shadowRoot.querySelector("ha-card");
const card = this.shadowRoot.querySelector(".card");
if (!surface || !card) return;
const width = surface.getBoundingClientRect().width;
if (!this._isReliableWidth(width)) {
this._schedulePostLayoutDensityPass();
if (!card.dataset.compact) card.dataset.compact = "normal";
return;
}
card.dataset.compact = this._classifyCompactTier(width, card.dataset.compact);
}
_applyLeftModeDensity() {
if (!this.shadowRoot) return;
this.shadowRoot.querySelectorAll(".main-line.left-mode").forEach((mainLine) => {
const width = mainLine.getBoundingClientRect().width;
if (!this._isReliableWidth(width)) {
this._schedulePostLayoutDensityPass();
if (!mainLine.dataset.leftDensity) mainLine.dataset.leftDensity = "normal";
return;
}
const labelText = mainLine.querySelector(".label-left-text");
const text = ((labelText == null ? void 0 : labelText.textContent) || "").trim();
const naturalLabelWidth = labelText ? this._measureTextWidthWithStyles(labelText, text) || labelText.scrollWidth : Number.POSITIVE_INFINITY;
const density = this._classifyLeftDensity(width, mainLine.dataset.leftDensity, naturalLabelWidth);
mainLine.dataset.leftDensity = density;
});
}
_applyInsideLabelDensity() {
if (!this.shadowRoot) return;
this.shadowRoot.querySelectorAll(".bar-inner-label").forEach((innerLabel) => {
var _a, _b, _c, _d, _e, _f;
const track = innerLabel.closest(".bar-track");
const mainLine = innerLabel.closest(".main-line");
const nameEl = innerLabel.querySelector(".inside-name");
const valueEl = innerLabel.querySelector(".inside-value");
if (!track || !nameEl || !valueEl) return;
const display = this._decodeDataAttr(valueEl.dataset.display || valueEl.textContent || "");
const unit = this._decodeDataAttr(valueEl.dataset.unit || "");
valueEl.dataset.valueFit = "normal";
innerLabel.dataset.valueFit = "normal";
const fullWidth = this._measureInsideValueMarkupWidth(valueEl, display, unit, false);
const numberWidth = this._measureInsideValueMarkupWidth(valueEl, display, unit, true);
const rowWidth = (_b = (_a = mainLine == null ? void 0 : mainLine.getBoundingClientRect) == null ? void 0 : _a.call(mainLine).width) != null ? _b : 0;
const rowDensity = this._isReliableWidth(rowWidth) ? this._classifyRowDensity(rowWidth, (_c = mainLine == null ? void 0 : mainLine.dataset) == null ? void 0 : _c.rowDensity) : ((_d = mainLine == null ? void 0 : mainLine.dataset) == null ? void 0 : _d.rowDensity) || "normal";
const iconWrap = (_f = (_e = mainLine == null ? void 0 : mainLine.querySelector) == null ? void 0 : _e.call(mainLine, ".icon-wrap")) != null ? _f : null;
const iconReserve = iconWrap ? this._getLeftModeIconWidth(iconWrap, mainLine) + this._getLeftModeGap(mainLine) : 0;
const minimum = this._getLeftModeBarMinWidth(mainLine);
const withIconWidth = this._isReliableWidth(rowWidth) ? Math.max(minimum, rowWidth - iconReserve) : track.getBoundingClientRect().width;
const withoutIconWidth = this._isReliableWidth(rowWidth) ? Math.max(minimum, rowWidth) : withIconWidth + iconReserve;
const nameText = (nameEl.textContent || "").trim();
const nameWidth = this._measureTextWidthWithStyles(nameEl, nameText) || nameEl.scrollWidth;
const candidate = (trackWidth) => {
const density = rowDensity === "compressed" ? "compressed" : this._classifyInsideDensity(trackWidth, fullWidth);
innerLabel.dataset.insideDensity = density;
innerLabel.dataset.valueFit = "normal";
valueEl.dataset.valueFit = "normal";
const padding = this._getNumericStyleValue(innerLabel, "padding-left", 0) + this._getNumericStyleValue(innerLabel, "padding-right", 0);
let cap = Math.max(0, trackWidth - padding);
const hideUnit = !!unit && fullWidth > cap;
let readingWidth = hideUnit ? numberWidth : fullWidth;
let valueFit = "normal";
if (readingWidth > cap) {
valueFit = "compact";
innerLabel.dataset.valueFit = valueFit;
valueEl.dataset.valueFit = valueFit;
cap = Math.max(0, trackWidth - this._getNumericStyleValue(innerLabel, "padding-left", 0) - this._getNumericStyleValue(innerLabel, "padding-right", 0));
readingWidth = this._measureInsideValueMarkupWidth(valueEl, display, unit, true);
}
const hideValue = readingWidth > cap;
const gap = hideValue ? 0 : this._getNumericStyleValue(innerLabel, "gap", 0);
const nameShare = density === "compact" ? 0.56 : density === "tight" ? 0.48 : 0.6;
const availableNameWidth = Math.max(
0,
Math.min(cap * nameShare, cap - (hideValue ? 0 : Math.ceil(readingWidth)) - gap)
);
let hideName = density === "dense" || density === "compressed";
if (nameText && nameWidth > 0) {
const usefulWidth = this._getInsideUsefulNameWidth(nameEl, nameText, nameWidth);
const visibleChars = this._measureVisibleLabelCharacters(nameEl, nameText, availableNameWidth);
hideName = availableNameWidth < usefulWidth || nameWidth > availableNameWidth + 1 && visibleChars < Math.min(4, nameText.length);
}
return {
density,
valueFit,
hideUnit,
hideValue,
hideName,
rank: hideValue ? 3 : valueFit === "compact" ? 2 : hideUnit ? 1 : 0
};
};
let hideIcon = rowDensity === "dense" || rowDensity === "compressed";
let chosen = candidate(hideIcon ? withoutIconWidth : withIconWidth);
if (iconWrap && !hideIcon) {
const withoutIcon = candidate(withoutIconWidth);
if (withoutIcon.rank < chosen.rank || withoutIcon.rank === chosen.rank && chosen.hideName && !withoutIcon.hideName) {
hideIcon = true;
chosen = withoutIcon;
}
}
innerLabel.dataset.insideDensity = chosen.density;
innerLabel.dataset.valueFit = chosen.valueFit;
innerLabel.dataset.hideName = chosen.hideName ? "true" : "false";
valueEl.dataset.valueFit = chosen.valueFit;
valueEl.dataset.hideUnit = chosen.hideUnit ? "true" : "false";
valueEl.dataset.hideValue = chosen.hideValue ? "true" : "false";
if (mainLine) mainLine.dataset.hideInsideIcon = hideIcon ? "true" : "false";
});
}
_classifyInsideDensity(trackWidth, valueWidth) {
if (trackWidth < Math.max(72, valueWidth + 12)) return "compressed";
if (trackWidth < valueWidth + 56) return "dense";
if (trackWidth < valueWidth + 92) return "tight";
if (trackWidth < valueWidth + 128) return "compact";
return "normal";
}
_applyRowDensity() {
if (!this.shadowRoot) return;
this.shadowRoot.querySelectorAll(".main-line").forEach((mainLine) => {
const width = mainLine.getBoundingClientRect().width;
if (!this._isReliableWidth(width)) {
this._schedulePostLayoutDensityPass();
if (!mainLine.dataset.rowDensity) mainLine.dataset.rowDensity = "normal";
return;
}
mainLine.dataset.rowDensity = this._classifyRowDensity(width, mainLine.dataset.rowDensity);
});
}
_applyAboveLabelDensity() {
if (!this.shadowRoot) return;
this.shadowRoot.querySelectorAll(".above-line, .hero-line").forEach((aboveLine) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
const label = aboveLine.querySelector(".above-bar-label, .hero-header");
if (!label) return;
const isHeroLine = aboveLine.classList.contains("hero-line");
const width = isHeroLine ? this._getHeroDensityWidth(aboveLine) : label.getBoundingClientRect().width;
let density = "normal";
if (width < 90) density = "xs";
else if (width < 110) density = "compressed";
else if (width < 150) density = "dense";
else if (width < 210) density = "tight";
else if (width < 280) density = "compact";
if (isHeroLine) {
aboveLine.dataset.heroDensity = density;
label.dataset.hideName = density === "dense" || density === "compressed" || density === "xs" ? "true" : "false";
aboveLine.dataset.hideHeroIcon = density === "compressed" || density === "xs" ? "true" : "false";
return;
}
aboveLine.dataset.aboveDensity = density;
const labelText = typeof label.querySelector === "function" ? label.querySelector(".above-bar-label-name") : null;
const valueEl = typeof label.querySelector === "function" ? label.querySelector(".above-bar-label-value") : null;
const display = this._decodeDataAttr(((_a = valueEl == null ? void 0 : valueEl.dataset) == null ? void 0 : _a.display) || "");
const unit = this._decodeDataAttr(((_b = valueEl == null ? void 0 : valueEl.dataset) == null ? void 0 : _b.unit) || "");
const row = (_c = aboveLine.closest) == null ? void 0 : _c.call(aboveLine, ".row");
const mainLine = (_d = row == null ? void 0 : row.querySelector) == null ? void 0 : _d.call(row, ".main-line.above-mode");
const rowDensity = ((_e = mainLine == null ? void 0 : mainLine.dataset) == null ? void 0 : _e.rowDensity) || "normal";
const iconWrap = (_g = (_f = mainLine == null ? void 0 : mainLine.querySelector) == null ? void 0 : _f.call(mainLine, ".icon-wrap")) != null ? _g : null;
const iconRect = (_h = iconWrap == null ? void 0 : iconWrap.getBoundingClientRect) == null ? void 0 : _h.call(iconWrap);
const iconStyle = iconWrap && typeof getComputedStyle === "function" ? getComputedStyle(iconWrap) : null;
const mainIconVisible = !!iconWrap && rowDensity !== "compressed" && (iconStyle == null ? void 0 : iconStyle.display) !== "none" && ((_i = iconRect == null ? void 0 : iconRect.width) != null ? _i : 0) > 0 && ((_j = iconRect == null ? void 0 : iconRect.height) != null ? _j : 0) > 0;
let hideName = density === "dense" || density === "compressed";
let hideSpacer = !mainIconVisible;
if (labelText && valueEl && display) {
const rowStack = (_k = aboveLine.closest) == null ? void 0 : _k.call(aboveLine, ".row-stack");
const lineWidth = (_o = (_n = (_l = rowStack == null ? void 0 : rowStack.getBoundingClientRect) == null ? void 0 : _l.call(rowStack).width) != null ? _n : (_m = aboveLine.getBoundingClientRect) == null ? void 0 : _m.call(aboveLine).width) != null ? _o : width;
const spacerEl = typeof aboveLine.querySelector === "function" ? aboveLine.querySelector(".above-icon-spacer") : null;
const spacerWidth = mainIconVisible ? iconRect.width : 0;
const lineGap = spacerEl ? this._getNumericStyleValue(aboveLine, "gap", 0) : 0;
const labelGap = this._getNumericStyleValue(aboveLine, "--sbcp-main-gap", this._getLeftModeGap(aboveLine));
const fullValueWidth = Math.ceil(this._measureValueMarkupWidth(valueEl, display, unit, false) + 2);
const valueOnlyWidth = Math.ceil(this._measureValueMarkupWidth(valueEl, display, unit, true) + 2);
const text = (labelText.textContent || "").trim();
const spacerReserve = mainIconVisible && spacerWidth > 0 ? spacerWidth + lineGap : 0;
const nameWidth = this._measureTextWidthWithStyles(labelText, text) || labelText.scrollWidth || 0;
const withNameBudget = Math.max(0, lineWidth - spacerReserve - labelGap);
const fitsWithName = (valueWidth, budget = withNameBudget) => {
const visibleWidth = Math.max(0, budget - valueWidth);
const visibleChars = this._measureVisibleLabelCharacters(labelText, text, visibleWidth);
return !this._shouldHideLeftLabel(text, nameWidth, visibleWidth, visibleChars);
};
const availableValueOnly = Math.max(0, lineWidth);
let hideUnit = false;
if (fullValueWidth <= withNameBudget && fitsWithName(fullValueWidth)) {
hideName = false;
hideSpacer = !mainIconVisible;
} else if (fullValueWidth <= availableValueOnly) {
hideName = !fitsWithName(fullValueWidth, lineWidth - labelGap);
hideSpacer = true;
} else if (valueOnlyWidth <= withNameBudget && fitsWithName(valueOnlyWidth)) {
hideName = false;
hideUnit = !!unit;
hideSpacer = !mainIconVisible;
} else if (fitsWithName(valueOnlyWidth, lineWidth - labelGap)) {
hideName = false;
hideSpacer = true;
hideUnit = !!unit;
} else {
hideName = true;
hideSpacer = true;
hideUnit = !!unit && fullValueWidth > availableValueOnly;
}
valueEl.dataset.hideUnit = hideUnit ? "true" : "false";
} else if (valueEl) {
valueEl.dataset.hideUnit = "false";
}
label.dataset.hideName = hideName ? "true" : "false";
aboveLine.dataset.hideAboveIcon = hideSpacer ? "true" : "false";
});
}
_getHeroDensityWidth(heroLine) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
const row = (_a = heroLine == null ? void 0 : heroLine.closest) == null ? void 0 : _a.call(heroLine, ".row");
const rowStack = (_b = heroLine == null ? void 0 : heroLine.closest) == null ? void 0 : _b.call(heroLine, ".row-stack");
const mainLine = (_c = rowStack == null ? void 0 : rowStack.querySelector) == null ? void 0 : _c.call(rowStack, ".main-line.hero-mode");
const barWrap = (_d = mainLine == null ? void 0 : mainLine.querySelector) == null ? void 0 : _d.call(mainLine, ".bar-wrap");
const candidates = [
(_e = row == null ? void 0 : row.getBoundingClientRect) == null ? void 0 : _e.call(row).width,
(_f = rowStack == null ? void 0 : rowStack.getBoundingClientRect) == null ? void 0 : _f.call(rowStack).width,
(_g = mainLine == null ? void 0 : mainLine.getBoundingClientRect) == null ? void 0 : _g.call(mainLine).width,
(_h = barWrap == null ? void 0 : barWrap.getBoundingClientRect) == null ? void 0 : _h.call(barWrap).width,
(_i = heroLine == null ? void 0 : heroLine.getBoundingClientRect) == null ? void 0 : _i.call(heroLine).width
];
for (const width of candidates) {
if (this._isReliableWidth(width, 8)) return width;
}
return 0;
}
_measureHeroValueWidth(heroLine, valueEl, valueFit = "normal", hideUnit = false) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
const layer = (_a = this.shadowRoot) == null ? void 0 : _a.querySelector(".measure-layer");
if (!layer || !heroLine || !valueEl) return 0;
const display = this._decodeDataAttr(valueEl.dataset.display || valueEl.textContent || "");
const unit = this._decodeDataAttr(valueEl.dataset.unit || "");
if (!display) return 0;
const wrapper = document.createElement("div");
wrapper.className = "hero-line";
wrapper.dataset.heroSize = heroLine.dataset.heroSize || "medium";
wrapper.dataset.heroDensity = heroLine.dataset.heroDensity || "normal";
wrapper.dataset.heroValueFit = valueFit;
wrapper.dataset.hideHeroUnit = hideUnit ? "true" : "false";
wrapper.style.display = "inline-block";
this._setStyleIfChanged(
wrapper,
"--sbcp-hero-base-size",
((_c = (_b = heroLine.style) == null ? void 0 : _b.getPropertyValue) == null ? void 0 : _c.call(_b, "--sbcp-hero-base-size")) || null
);
const measureValue = document.createElement("span");
measureValue.className = "hero-value";
measureValue.dataset.display = valueEl.dataset.display || "";
measureValue.dataset.unit = valueEl.dataset.unit || "";
measureValue.innerHTML = this._formatRightValueMarkup(display, unit, hideUnit);
measureValue.style.display = "inline-flex";
measureValue.style.flex = "0 0 auto";
measureValue.style.width = "auto";
measureValue.style.minWidth = "0";
measureValue.style.maxWidth = "none";
measureValue.style.justifyContent = "flex-start";
measureValue.style.justifySelf = "start";
measureValue.style.overflow = "visible";
const text = measureValue.querySelector(".value-right-text");
if (text) {
text.style.display = "inline-flex";
text.style.flex = "0 0 auto";
text.style.width = "auto";
text.style.minWidth = "0";
text.style.maxWidth = "none";
text.style.justifyContent = "flex-start";
text.style.overflow = "visible";
}
const number = measureValue.querySelector(".value-right-number");
if (number) {
number.style.flex = "0 0 auto";
number.style.minWidth = "0";
number.style.overflow = "visible";
number.style.textOverflow = "clip";
}
const unitGroup = measureValue.querySelector(".unit-group");
if (unitGroup) {
unitGroup.style.flex = "0 0 auto";
unitGroup.style.minWidth = "0";
unitGroup.style.overflow = "visible";
}
wrapper.appendChild(measureValue);
layer.replaceChildren(wrapper);
return Math.max(
Math.ceil((_e = (_d = measureValue.getBoundingClientRect) == null ? void 0 : _d.call(measureValue).width) != null ? _e : 0),
Math.ceil(measureValue.scrollWidth || 0),
Math.ceil((_h = (_g = (_f = text == null ? void 0 : text.getBoundingClientRect) == null ? void 0 : _f.call(text).width) != null ? _g : text == null ? void 0 : text.scrollWidth) != null ? _h : 0),
Math.ceil((_k = (_j = (_i = number == null ? void 0 : number.getBoundingClientRect) == null ? void 0 : _i.call(number).width) != null ? _j : number == null ? void 0 : number.scrollWidth) != null ? _k : 0),
Math.ceil((_n = (_m = (_l = unitGroup == null ? void 0 : unitGroup.getBoundingClientRect) == null ? void 0 : _l.call(unitGroup).width) != null ? _m : unitGroup == null ? void 0 : unitGroup.scrollWidth) != null ? _n : 0)
);
}
_applyHeroValueFit() {
if (!this.shadowRoot) return;
this.shadowRoot.querySelectorAll(".hero-line").forEach((heroLine) => {
var _a, _b;
const headerEl = heroLine.querySelector(".hero-header");
const labelEl = heroLine.querySelector(".hero-label");
const valueEl = heroLine.querySelector(".hero-value");
const unitGroup = heroLine.querySelector(".unit-group");
if (!headerEl || !valueEl) return;
heroLine.dataset.hideHeroUnit = "false";
heroLine.dataset.heroValueFit = "normal";
delete headerEl.dataset.priorityHideName;
const headerWidth = Math.floor((_b = (_a = headerEl.getBoundingClientRect) == null ? void 0 : _a.call(headerEl).width) != null ? _b : 0);
if (!this._isReliableWidth(headerWidth, 8)) {
this._schedulePostLayoutDensityPass();
return;
}
const hasUnit = !!unitGroup && unitGroup.textContent.trim().length > 0;
const labelHiddenByDensity = headerEl.dataset.hideName === "true";
const visibleLabelGap = !labelHiddenByDensity && labelEl ? this._getNumericStyleValue(headerEl, "column-gap", 0) : 0;
const valueWidth = (valueFit = "normal", hideUnit = false) => this._measureHeroValueWidth(heroLine, valueEl, valueFit, hideUnit);
const availableWithLabel = Math.max(0, headerWidth - visibleLabelGap - 4);
const availableWithoutLabel = Math.max(0, headerWidth - 4);
if (!this._isReliableWidth(availableWithoutLabel, 8)) {
heroLine.dataset.hideHeroUnit = hasUnit ? "true" : "false";
heroLine.dataset.heroValueFit = "minimum";
this._schedulePostLayoutDensityPass();
return;
}
if (valueWidth("normal", false) <= availableWithLabel) return;
if (!labelHiddenByDensity && labelEl) {
headerEl.dataset.priorityHideName = "true";
if (valueWidth("normal", false) <= availableWithoutLabel) return;
}
if (hasUnit) {
for (const fit of ["tight", "minimum"]) {
const readingWidth = valueWidth(fit, false);
if (readingWidth <= availableWithoutLabel) {
heroLine.dataset.heroValueFit = fit;
if (!labelHiddenByDensity && labelEl) {
const text = (labelEl.textContent || "").trim();
const nameWidth = this._measureTextWidthWithStyles(labelEl, text);
const availableName = Math.max(0, availableWithLabel - readingWidth);
if (!this._shouldHideLeftLabel(
text,
nameWidth,
availableName,
this._measureVisibleLabelCharacters(labelEl, text, availableName)
)) {
delete headerEl.dataset.priorityHideName;
}
}
return;
}
}
heroLine.dataset.hideHeroUnit = "true";
if (valueWidth("normal", true) <= availableWithoutLabel) return;
}
heroLine.dataset.heroValueFit = "tight";
if (valueWidth("tight", hasUnit) <= availableWithoutLabel) return;
heroLine.dataset.heroValueFit = "minimum";
if (valueWidth("minimum", hasUnit) <= availableWithoutLabel) return;
heroLine.dataset.heroValueFit = "hidden";
});
}
_measureValueMarkupWidth(valueEl, display, unit, hideUnit) {
var _a;
const layer = (_a = this.shadowRoot) == null ? void 0 : _a.querySelector(".measure-layer");
if (!layer || !valueEl) return 0;
const clone = valueEl.cloneNode(false);
clone.removeAttribute("data-hide-unit");
clone.style.removeProperty("--sbcp-value-extra-width");
clone.style.width = "auto";
clone.style.minWidth = "0";
clone.style.maxWidth = "none";
clone.style.flex = "0 0 auto";
clone.innerHTML = this._formatRightValueMarkup(display, unit, hideUnit);
layer.replaceChildren(clone);
return clone.scrollWidth;
}
_measureInsideValueMarkupWidth(valueEl, display, unit, hideUnit = false) {
var _a;
const layer = (_a = this.shadowRoot) == null ? void 0 : _a.querySelector(".measure-layer");
if (!layer || !valueEl) return (valueEl == null ? void 0 : valueEl.scrollWidth) || 0;
const clone = valueEl.cloneNode(false);
clone.style.width = "auto";
clone.style.minWidth = "0";
clone.style.maxWidth = "none";
clone.style.flex = "0 0 auto";
clone.style.overflow = "visible";
clone.style.textOverflow = "clip";
clone.style.whiteSpace = "nowrap";
clone.innerHTML = this._formatInsideValueMarkup(display, unit, hideUnit);
clone.removeAttribute("data-hide-value");
const wrapper = document.createElement("div");
wrapper.className = "bar-inner-label";
wrapper.style.cssText = "position:static;display:block;padding:0";
wrapper.appendChild(clone);
layer.replaceChildren(wrapper);
return clone.getBoundingClientRect().width;
}
_measureTextWidthWithStyles(sourceEl, text) {
var _a;
const layer = (_a = this.shadowRoot) == null ? void 0 : _a.querySelector(".measure-layer");
if (!layer || !sourceEl) return 0;
const clone = sourceEl.cloneNode(false);
clone.textContent = text;
clone.style.width = "auto";
clone.style.minWidth = "0";
clone.style.maxWidth = "none";
clone.style.flex = "0 0 auto";
clone.style.overflow = "visible";
clone.style.textOverflow = "clip";
clone.style.whiteSpace = "nowrap";
layer.replaceChildren(clone);
return clone.scrollWidth;
}
_measureVisibleLabelCharacters(labelTextEl, text, visibleWidth) {
if (!labelTextEl || !text || !Number.isFinite(visibleWidth) || visibleWidth <= 0) return 0;
const ellipsisWidth = this._measureTextWidthWithStyles(labelTextEl, "...");
const availableTextWidth = Math.max(0, visibleWidth - ellipsisWidth);
if (availableTextWidth <= 0) return 0;
let low = 0;
let high = text.length;
while (low < high) {
const mid = Math.ceil((low + high) / 2);
const width = this._measureTextWidthWithStyles(labelTextEl, text.slice(0, mid));
if (width <= availableTextWidth) {
low = mid;
} else {
high = mid - 1;
}
}
return low;
}
_shouldHideLeftLabel(text, fullWidth, visibleWidth, visibleChars) {
if (!text) return false;
if (!Number.isFinite(fullWidth) || !Number.isFinite(visibleWidth)) return false;
const truncated = fullWidth > visibleWidth + 1;
return truncated && visibleChars < 5;
}
_getInsideUsefulNameWidth(labelTextEl, text, fullWidth = NaN) {
if (!text) return 0;
const naturalWidth = Number.isFinite(fullWidth) && fullWidth > 0 ? fullWidth : this._measureTextWidthWithStyles(labelTextEl, text);
const usefulChars = text.slice(0, Math.min(5, text.length));
const usefulWidth = this._measureTextWidthWithStyles(labelTextEl, usefulChars) + this._measureTextWidthWithStyles(labelTextEl, "...");
const minUsefulWidth = Math.max(36, Math.min(44, usefulWidth || 0));
return Math.min(naturalWidth || minUsefulWidth, minUsefulWidth);
}
_applyValueWidthReservation() {
if (!this.shadowRoot) return;
this.shadowRoot.querySelectorAll(".value-right").forEach((valueEl) => {
var _a, _b, _c, _d, _e;
const display = this._decodeDataAttr(valueEl.dataset.display || "");
const unit = this._decodeDataAttr(valueEl.dataset.unit || "");
if (!display) {
valueEl.style.setProperty("--sbcp-value-extra-width", "0px");
return;
}
const getStyle = typeof globalThis.getComputedStyle === "function" && globalThis.getComputedStyle.bind(globalThis) || typeof window !== "undefined" && typeof window.getComputedStyle === "function" && window.getComputedStyle.bind(window) || ((_c = (_b = (_a = valueEl == null ? void 0 : valueEl.ownerDocument) == null ? void 0 : _a.defaultView) == null ? void 0 : _b.getComputedStyle) == null ? void 0 : _c.bind(valueEl.ownerDocument.defaultView));
if (!getStyle) return;
const style = getStyle(valueEl);
const baseWidth = parseFloat(style.getPropertyValue("--sbcp-value-width")) || valueEl.clientWidth || 0;
const fullWidth = Math.ceil(this._measureValueMarkupWidth(valueEl, display, unit, false) + 2);
const mainLine = valueEl.closest(".main-line");
let desiredWidth = fullWidth;
if (mainLine == null ? void 0 : mainLine.classList.contains("off-mode")) {
const barWrap = mainLine.querySelector(".bar-wrap");
const iconWrap = mainLine.querySelector(".icon-wrap");
mainLine.dataset.hideOffIcon = "false";
const gap = parseFloat(getStyle(mainLine).gap) || 0;
const rowWidth = (_e = (_d = mainLine.getBoundingClientRect) == null ? void 0 : _d.call(mainLine).width) != null ? _e : 0;
const minimumRail = parseFloat(getStyle(barWrap).minWidth) || 0;
const iconVisible = iconWrap && getStyle(iconWrap).display !== "none";
const iconWidth = iconVisible ? this._getLeftModeIconWidth(iconWrap, mainLine) : 0;
const withoutIcon = Math.max(0, rowWidth - minimumRail - gap);
const withIcon = withoutIcon - (iconVisible ? iconWidth + gap : 0);
const numberWidth = Math.ceil(this._measureValueMarkupWidth(valueEl, display, unit, true) + 2);
const hideIcon = iconVisible && (fullWidth > withIcon && fullWidth <= withoutIcon || numberWidth > withIcon && numberWidth <= withoutIcon);
mainLine.dataset.hideOffIcon = hideIcon ? "true" : "false";
const availableWidth = hideIcon ? withoutIcon : withIcon;
if (availableWidth > 0) {
const readableWidth = fullWidth <= availableWidth ? fullWidth : numberWidth;
desiredWidth = Math.min(readableWidth, availableWidth);
}
}
const extraWidth = Math.max(0, desiredWidth - baseWidth);
valueEl.style.setProperty("--sbcp-value-extra-width", `${extraWidth}px`);
});
}
_applyValueVisibility() {
if (!this.shadowRoot) return;
this.shadowRoot.querySelectorAll(".value-right, .top-right-value, .above-bar-label-value").forEach((valueEl) => {
var _a, _b, _c, _d;
const display = this._decodeDataAttr(valueEl.dataset.display || "");
const unit = this._decodeDataAttr(valueEl.dataset.unit || "");
let hideUnit = valueEl.dataset.hideUnit === "true";
const isAboveValue = (_a = valueEl.classList) == null ? void 0 : _a.contains("above-bar-label-value");
if (!isAboveValue && display) {
const availableWidth = (_d = (_c = (_b = valueEl.getBoundingClientRect) == null ? void 0 : _b.call(valueEl).width) != null ? _c : valueEl.clientWidth) != null ? _d : 0;
const fullValueWidth = Math.ceil(this._measureValueMarkupWidth(valueEl, display, unit, false) + 2);
hideUnit = !!unit && availableWidth > 0 ? fullValueWidth > availableWidth : false;
valueEl.dataset.hideUnit = hideUnit ? "true" : "false";
}
if (valueEl.innerHTML !== this._formatRightValueMarkup(display, unit, hideUnit)) {
valueEl.innerHTML = this._formatRightValueMarkup(display, unit, hideUnit);
}
});
this.shadowRoot.querySelectorAll(".inside-value").forEach((valueEl) => {
const display = this._decodeDataAttr(valueEl.dataset.display || "");
const unit = this._decodeDataAttr(valueEl.dataset.unit || "");
const hideUnit = valueEl.dataset.hideUnit === "true";
const hideValue = valueEl.dataset.hideValue === "true";
if (!hideValue && !display) return;
if (!valueEl.dataset.hideUnit) valueEl.dataset.hideUnit = "false";
if (!valueEl.dataset.hideValue) valueEl.dataset.hideValue = "false";
const nextMarkup = this._formatInsideValueMarkup(display, unit, hideUnit);
if (valueEl.innerHTML !== nextMarkup) {
valueEl.innerHTML = nextMarkup;
}
});
}
_getMinimumBarShare() {
return 0.5;
}
_getMinimumBarShareHysteresis() {
return 0.02;
}
_getTopValueEnableShare() {
return this._getMinimumBarShare() - this._getMinimumBarShareHysteresis();
}
_getTopValueDisableShare() {
return this._getMinimumBarShare() + this._getMinimumBarShareHysteresis();
}
_getNumericStyleValue(el, propertyName, fallback = 0) {
if (!el) return fallback;
try {
const value = parseFloat(getComputedStyle(el).getPropertyValue(propertyName));
return Number.isFinite(value) ? value : fallback;
} catch (_err) {
return fallback;
}
}
_getLeftModeGap(mainLine) {
var _a;
const computedGap = this._getNumericStyleValue(mainLine, "gap", NaN);
if (Number.isFinite(computedGap)) return computedGap;
const density = ((_a = mainLine == null ? void 0 : mainLine.dataset) == null ? void 0 : _a.rowDensity) || "normal";
if (density === "tight") return 7;
if (density === "dense" || density === "compressed") return 6;
return 8;
}
_getLeftModeBarMinWidth(mainLine) {
var _a;
const computedMin = this._getNumericStyleValue(mainLine, "--sbcp-bar-min-width", NaN);
if (Number.isFinite(computedMin)) return computedMin;
const density = ((_a = mainLine == null ? void 0 : mainLine.dataset) == null ? void 0 : _a.leftDensity) || "normal";
if (density === "compact") return 52;
if (density === "tight") return 48;
if (density === "dense") return 44;
if (density === "compressed") return 40;
return 56;
}
_getLeftModeIconWidth(iconWrap, mainLine) {
var _a, _b;
const measured = (_a = iconWrap == null ? void 0 : iconWrap.getBoundingClientRect) == null ? void 0 : _a.call(iconWrap).width;
if (this._isReliableWidth(measured, 1)) return measured;
const computed = this._getNumericStyleValue(iconWrap || mainLine, "--sbcp-icon-width", NaN);
if (Number.isFinite(computed)) return computed;
const density = ((_b = mainLine == null ? void 0 : mainLine.dataset) == null ? void 0 : _b.leftDensity) || "normal";
if (density === "compact") return 26;
if (density === "tight") return 24;
if (density === "dense") return 23;
if (density === "compressed") return 22;
return 28;
}
_getReservedInlineValueWidth(valueEl) {
var _a, _b, _c;
if (!valueEl) return 0;
const display = this._decodeDataAttr(valueEl.dataset.display || "");
const unit = this._decodeDataAttr(valueEl.dataset.unit || "");
const baseWidth = this._getNumericStyleValue(valueEl, "--sbcp-value-width", valueEl.clientWidth || 0);
const inlineExtra = parseFloat(((_b = (_a = valueEl.style) == null ? void 0 : _a.getPropertyValue) == null ? void 0 : _b.call(_a, "--sbcp-value-extra-width")) || ((_c = valueEl.style) == null ? void 0 : _c["--sbcp-value-extra-width"]) || "0");
const extraWidth = Number.isFinite(inlineExtra) ? inlineExtra : this._getNumericStyleValue(valueEl, "--sbcp-value-extra-width", 0);
const reservedWidth = Math.max(0, baseWidth + extraWidth);
if (!display) return reservedWidth;
const fullMarkupWidth = Math.ceil(this._measureValueMarkupWidth(valueEl, display, unit, false) + 2);
return Math.max(reservedWidth, fullMarkupWidth);
}
_getStableLeftLabelMetrics(row, rowWidth = null) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
const mainLine = row == null ? void 0 : row.querySelector(".main-line");
const labelWrap = row == null ? void 0 : row.querySelector(".label-left");
const labelText = row == null ? void 0 : row.querySelector(".label-left-text");
if (!mainLine || !labelWrap || !labelText) return null;
const width = (_b = rowWidth != null ? rowWidth : (_a = mainLine.getBoundingClientRect) == null ? void 0 : _a.call(mainLine).width) != null ? _b : 0;
const text = (labelText.textContent || "").trim();
const naturalWidth = this._measureTextWidthWithStyles(labelText, text) || labelText.scrollWidth || 0;
const density = ((_c = mainLine.dataset) == null ? void 0 : _c.leftDensity) || "normal";
const labelShares = { normal: 0.25, compact: 0.22, tight: 0.19, dense: 0.16, compressed: 0.14 };
const entityConfig = (_e = (_d = this._config) == null ? void 0 : _d.entities) == null ? void 0 : _e.find((item) => {
var _a2;
return item.entity === ((_a2 = row.dataset) == null ? void 0 : _a2.entity);
});
const configuredWidth = entityConfig ? (_i = (_h = (_g = (_f = this._resolve(entityConfig)) == null ? void 0 : _f.layout) == null ? void 0 : _g.label) == null ? void 0 : _h.width) != null ? _i : 100 : 100;
const maximumWidth = width > 0 ? Math.min(configuredWidth, width * ((_j = labelShares[density]) != null ? _j : labelShares.normal)) : Math.min(naturalWidth, configuredWidth);
return { text, naturalWidth, maximumWidth, labelWidth: maximumWidth, labelText };
}
_estimateLeftModeWidthBudget(row) {
var _a, _b, _c;
const mainLine = row == null ? void 0 : row.querySelector(".main-line");
if (!mainLine) return null;
const rowWidth = (_b = (_a = mainLine.getBoundingClientRect) == null ? void 0 : _a.call(mainLine).width) != null ? _b : 0;
if (!this._isReliableWidth(rowWidth)) return null;
const labelWrap = row.querySelector(".label-left");
const iconWrap = row.querySelector(".icon-wrap");
const valueEl = row.querySelector(".value-right");
const labelMetrics = this._getStableLeftLabelMetrics(row, rowWidth);
const labelWidth = (_c = labelMetrics == null ? void 0 : labelMetrics.labelWidth) != null ? _c : 0;
const iconWidth = iconWrap ? this._getLeftModeIconWidth(iconWrap, mainLine) : 0;
const valueWidth = this._getReservedInlineValueWidth(valueEl);
const gap = this._getLeftModeGap(mainLine);
const barMinWidth = this._getLeftModeBarMinWidth(mainLine);
const baseLabelVisible = !!labelWrap;
const hasIcon = !!iconWrap;
const labelGapCount = hasIcon ? 2 : 1;
const labelAvailableWidth = Math.max(
0,
rowWidth - (hasIcon ? iconWidth : 0) - barMinWidth - labelGapCount * gap
);
const candidateLabelWidth = Math.min(labelWidth, labelAvailableWidth);
const labelSacrificial = !labelMetrics || this._shouldHideLeftLabel(
labelMetrics.text,
labelMetrics.naturalWidth,
candidateLabelWidth,
this._measureVisibleLabelCharacters(labelMetrics.labelText, labelMetrics.text, candidateLabelWidth)
);
const withoutIconLabelWidth = Math.min(labelWidth, Math.max(0, rowWidth - barMinWidth - gap));
const labelSacrificialWithoutIcon = !labelMetrics || this._shouldHideLeftLabel(
labelMetrics.text,
labelMetrics.naturalWidth,
withoutIconLabelWidth,
this._measureVisibleLabelCharacters(labelMetrics.labelText, labelMetrics.text, withoutIconLabelWidth)
);
return {
rowWidth,
gap,
barMinWidth,
labelSacrificialWithoutIcon,
labelWidth: this._isReliableWidth(labelWidth, 0) ? labelWidth : 0,
iconWidth: this._isReliableWidth(iconWidth, 0) ? iconWidth : 0,
valueWidth: this._isReliableWidth(valueWidth, 0) ? valueWidth : 0,
baseLabelVisible,
labelSacrificial,
hasIcon,
mainLine,
iconWrap,
valueEl,
rowStack: row.querySelector(".row-stack")
};
}
_predictLeftModeBarShareForState(row, state, budget = null) {
const effectiveBudget = budget || this._estimateLeftModeWidthBudget(row);
if (!effectiveBudget) return null;
const showLabel = effectiveBudget.baseLabelVisible && !state.hideLabel;
const showIcon = effectiveBudget.hasIcon && !state.hideIcon;
const showInlineValue = !state.topValue;
const visibleItems = 1 + (showIcon ? 1 : 0) + (showLabel ? 1 : 0) + (showInlineValue ? 1 : 0);
const gapCount = Math.max(0, visibleItems - 1);
const reservedWidth = (showIcon ? effectiveBudget.iconWidth : 0) + (showLabel ? effectiveBudget.labelWidth : 0) + (showInlineValue ? effectiveBudget.valueWidth : 0) + gapCount * effectiveBudget.gap;
const remainingWidth = Math.max(0, effectiveBudget.rowWidth - reservedWidth);
const fits = remainingWidth >= effectiveBudget.barMinWidth;
const predictedBarWidth = fits ? remainingWidth : effectiveBudget.barMinWidth;
return {
rowWidth: effectiveBudget.rowWidth,
barWidth: predictedBarWidth,
share: predictedBarWidth / effectiveBudget.rowWidth,
showLabel,
showIcon,
showInlineValue,
reservedWidth,
gapCount,
fits
};
}
_getLeftModeCandidateStates(budget) {
const states = [];
if (budget && !budget.labelSacrificial) {
states.push(
{ hideLabel: false, topValue: false, hideIcon: false },
{ hideLabel: false, topValue: true, hideIcon: false }
);
}
if (budget && !budget.labelSacrificialWithoutIcon) {
states.push(
{ hideLabel: false, topValue: false, hideIcon: true },
{ hideLabel: false, topValue: true, hideIcon: true }
);
}
states.push(
{ hideLabel: true, topValue: false, hideIcon: false },
{ hideLabel: true, topValue: false, hideIcon: true },
{ hideLabel: true, topValue: true, hideIcon: true }
);
return states;
}
_chooseFallbackPredictedLeftModeState(row, states, budget) {
let fallback = null;
for (const state of states) {
const predicted = this._predictLeftModeBarShareForState(row, state, budget);
if (!predicted) continue;
fallback = { ...state, predicted };
}
return fallback;
}
_chooseLeftModeResponsiveState(row, settled = true) {
var _a, _b, _c, _d;
const budget = this._estimateLeftModeWidthBudget(row);
if (!budget) return null;
const minimumBarShare = this._getMinimumBarShare();
const states = this._getLeftModeCandidateStates(budget);
const entityCfg = (_b = this._config.entities) == null ? void 0 : _b[Number((_a = row == null ? void 0 : row.dataset) == null ? void 0 : _a.rowIndex)];
const previousTopValue = entityCfg && this._leftModeResponsiveHistory.has(entityCfg) ? this._leftModeResponsiveHistory.get(entityCfg) : ((_d = (_c = budget.rowStack) == null ? void 0 : _c.dataset) == null ? void 0 : _d.forceTopValue) === "true";
const topStates = states.filter((state) => state.topValue);
const enableShare = this._getTopValueEnableShare();
const disableShare = this._getTopValueDisableShare();
for (const state of states) {
const threshold = state.topValue || settled ? minimumBarShare : previousTopValue ? disableShare : enableShare;
const predicted = this._predictLeftModeBarShareForState(row, state, budget);
if ((predicted == null ? void 0 : predicted.fits) && predicted.share >= threshold) return { ...state, predicted };
}
return this._chooseFallbackPredictedLeftModeState(row, topStates, budget);
}
_applyLeftModeResponsiveState(row, state) {
var _a, _b;
const mainLine = row == null ? void 0 : row.querySelector(".main-line");
const rowStack = row == null ? void 0 : row.querySelector(".row-stack");
const leftLabel = row == null ? void 0 : row.querySelector(".label-left");
if (!mainLine || !rowStack) return;
const entityCfg = (_b = this._config.entities) == null ? void 0 : _b[Number((_a = row == null ? void 0 : row.dataset) == null ? void 0 : _a.rowIndex)];
if (entityCfg) this._leftModeResponsiveHistory.set(entityCfg, (state == null ? void 0 : state.topValue) === true);
delete rowStack.dataset.forceTopValue;
delete mainLine.dataset.hideLeftIcon;
if (leftLabel) delete leftLabel.dataset.priorityHidden;
if ((state == null ? void 0 : state.hideLabel) && leftLabel) {
leftLabel.dataset.priorityHidden = "true";
}
if (state == null ? void 0 : state.topValue) {
rowStack.dataset.forceTopValue = "true";
}
if (state == null ? void 0 : state.hideIcon) {
mainLine.dataset.hideLeftIcon = "true";
}
}
_getMeasuredBarShare(row) {
const mainLine = row == null ? void 0 : row.querySelector(".main-line");
const track = row == null ? void 0 : row.querySelector(".bar-track");
if (!mainLine || !track) return null;
const rowWidth = mainLine.getBoundingClientRect().width;
const barWidth = track.getBoundingClientRect().width;
if (!this._isReliableWidth(rowWidth) || !this._isReliableWidth(barWidth, 1)) return null;
return { rowWidth, barWidth, share: barWidth / rowWidth, mainLine, track };
}
_clearMinimumBarShareOverrides(row) {
const mainLine = row == null ? void 0 : row.querySelector(".main-line");
const rowStack = row == null ? void 0 : row.querySelector(".row-stack");
const leftLabel = row == null ? void 0 : row.querySelector(".label-left");
const aboveLabel = row == null ? void 0 : row.querySelector(".above-bar-label");
const innerLabel = row == null ? void 0 : row.querySelector(".bar-inner-label");
const aboveLine = row == null ? void 0 : row.querySelector(".above-line");
if (rowStack) delete rowStack.dataset.forceTopValue;
if (leftLabel) delete leftLabel.dataset.priorityHidden;
if (aboveLabel) delete aboveLabel.dataset.priorityHideName;
if (innerLabel) delete innerLabel.dataset.priorityHideName;
if (!mainLine) return;
delete mainLine.dataset.hideLeftIcon;
delete mainLine.dataset.hideAboveIcon;
delete mainLine.dataset.priorityHideInsideIcon;
}
_hideMinimumBarShareLabel(row, mode) {
if (mode === "left") {
const leftLabel = row.querySelector(".label-left");
if (leftLabel) leftLabel.dataset.priorityHidden = "true";
return;
}
if (mode === "above") {
const aboveLabel = row.querySelector(".above-bar-label");
if (aboveLabel) aboveLabel.dataset.priorityHideName = "true";
return;
}
if (mode === "inside") {
const innerLabel = row.querySelector(".bar-inner-label");
if (innerLabel) innerLabel.dataset.priorityHideName = "true";
}
}
_forceMinimumBarShareTopValue(row, mode) {
if (mode !== "left") return;
const rowStack = row.querySelector(".row-stack");
if (rowStack) rowStack.dataset.forceTopValue = "true";
}
_hideMinimumBarShareIcon(row, mode) {
const mainLine = row.querySelector(".main-line");
if (!mainLine) return;
if (mode === "left") {
mainLine.dataset.hideLeftIcon = "true";
return;
}
if (mode === "above") {
mainLine.dataset.hideAboveIcon = "true";
const aboveLine = row.querySelector(".above-line");
if (aboveLine) aboveLine.dataset.hideAboveIcon = "true";
return;
}
if (mode === "inside") {
mainLine.dataset.priorityHideInsideIcon = "true";
}
}
_getLabelSacrificeMetrics(row, mode, measurement) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
const rowWidth = (_d = (_c = measurement == null ? void 0 : measurement.rowWidth) != null ? _c : (_b = (_a = row == null ? void 0 : row.querySelector(".main-line")) == null ? void 0 : _a.getBoundingClientRect) == null ? void 0 : _b.call(_a).width) != null ? _d : 0;
if (!this._isReliableWidth(rowWidth)) return null;
if (mode === "left") {
const labelWrap = row.querySelector(".label-left");
const labelText = row.querySelector(".label-left-text");
if (!labelWrap || !labelText) return null;
const text = (labelText.textContent || "").trim();
const visibleWidth = labelText.clientWidth;
const fullWidth = labelText.scrollWidth;
const visibleChars = this._measureVisibleLabelCharacters(labelText, text, visibleWidth);
const labelWidth = (_f = (_e = labelWrap.getBoundingClientRect) == null ? void 0 : _e.call(labelWrap).width) != null ? _f : visibleWidth;
return { text, visibleWidth, fullWidth, visibleChars, labelWidth, rowWidth };
}
if (mode === "above") {
const labelText = row.querySelector(".above-bar-label-name");
if (!labelText) return null;
const text = (labelText.textContent || "").trim();
const visibleWidth = labelText.clientWidth;
const fullWidth = labelText.scrollWidth;
const visibleChars = this._measureVisibleLabelCharacters(labelText, text, visibleWidth);
const labelWidth = (_h = (_g = labelText.getBoundingClientRect) == null ? void 0 : _g.call(labelText).width) != null ? _h : visibleWidth;
return { text, visibleWidth, fullWidth, visibleChars, labelWidth, rowWidth };
}
if (mode === "inside") {
const labelText = row.querySelector(".inside-name");
if (!labelText) return null;
const text = (labelText.textContent || "").trim();
const visibleWidth = labelText.clientWidth;
const fullWidth = labelText.scrollWidth;
const visibleChars = this._measureVisibleLabelCharacters(labelText, text, visibleWidth);
const labelWidth = (_j = (_i = labelText.getBoundingClientRect) == null ? void 0 : _i.call(labelText).width) != null ? _j : visibleWidth;
return { text, visibleWidth, fullWidth, visibleChars, labelWidth, rowWidth };
}
return null;
}
_isLabelWorthSacrificing(row, mode, measurement) {
const metrics = this._getLabelSacrificeMetrics(row, mode, measurement);
if (!metrics || !metrics.text) return false;
return this._shouldHideLeftLabel(metrics.text, metrics.fullWidth, metrics.visibleWidth, metrics.visibleChars);
}
_ensureMinimumBarShare(rows = null, leftWidths = null) {
if (!this.shadowRoot) return;
const targetRows = rows || this.shadowRoot.querySelectorAll(".row[data-entity]");
const minimumBarShare = this._getMinimumBarShare();
targetRows.forEach((row) => {
const mainLine = row.querySelector(".main-line");
if (!mainLine) return;
const mode = mainLine.classList.contains("left-mode") ? "left" : mainLine.classList.contains("above-mode") ? "above" : mainLine.classList.contains("inside-mode") ? "inside" : "other";
if (mode === "other") return;
if (mode === "left") {
const settled = !leftWidths || leftWidths.get(mainLine) === mainLine.getBoundingClientRect().width;
const state = this._chooseLeftModeResponsiveState(row, settled);
if (state) this._applyLeftModeResponsiveState(row, state);
if (!settled) this._schedulePostLayoutDensityPass();
return;
}
this._clearMinimumBarShareOverrides(row);
let measurement = this._getMeasuredBarShare(row);
if (!measurement || measurement.share >= minimumBarShare) return;
if (this._isLabelWorthSacrificing(row, mode, measurement)) {
this._hideMinimumBarShareLabel(row, mode);
measurement = this._getMeasuredBarShare(row);
if (!measurement || measurement.share >= minimumBarShare) return;
}
this._forceMinimumBarShareTopValue(row, mode);
this._applyTopRightValueLayout();
measurement = this._getMeasuredBarShare(row);
if (!measurement || measurement.share >= minimumBarShare) return;
this._hideMinimumBarShareIcon(row, mode);
});
}
_shouldUseTopValueRow(mainLine) {
var _a, _b, _c;
if (!((_a = mainLine == null ? void 0 : mainLine.classList) == null ? void 0 : _a.contains("left-mode"))) return false;
return ((_c = (_b = mainLine.closest) == null ? void 0 : _b.call(mainLine, ".row-stack")) == null ? void 0 : _c.dataset.forceTopValue) === "true";
}
_getAdaptiveDensityForMainLine(mainLine) {
var _a;
if (!mainLine) return "normal";
if ((_a = mainLine.classList) == null ? void 0 : _a.contains("left-mode")) {
return mainLine.dataset.leftDensity || "normal";
}
return mainLine.dataset.rowDensity || "normal";
}
_getAdaptiveDefaultHeightForDensity(density) {
if (density === "compressed") return 24;
if (density === "dense") return 28;
return 38;
}
_getEffectiveRowHeight(baseHeight, heightExplicit, mainLine) {
if (heightExplicit) return this._clampSupportedRowHeight(baseHeight);
return this._clampSupportedRowHeight(this._getAdaptiveDefaultHeightForDensity(this._getAdaptiveDensityForMainLine(mainLine)));
}
_applyAdaptiveRowHeight() {
if (!this.shadowRoot) return;
this.shadowRoot.querySelectorAll(".row[data-entity]").forEach((row) => {
const mainLine = row.querySelector(".main-line");
const rowStack = row.querySelector(".row-stack");
if (!mainLine) return;
const baseHeight = parseFloat(row.dataset.baseHeight || "38") || 38;
const explicit = row.dataset.heightExplicit === "true";
const effectiveHeight = this._getEffectiveRowHeight(baseHeight, explicit, mainLine);
row.style.setProperty("--sbcp-row-height", `${effectiveHeight}px`);
if (rowStack) rowStack.style.setProperty("--sbcp-row-height", `${effectiveHeight}px`);
mainLine.style.height = `${effectiveHeight}px`;
const labelLeft = mainLine.querySelector(".label-left");
if (labelLeft) labelLeft.style.height = `${effectiveHeight}px`;
const iconWrap = mainLine.querySelector(".icon-wrap");
if (iconWrap) {
iconWrap.style.height = `${effectiveHeight}px`;
iconWrap.style.minHeight = `${effectiveHeight}px`;
}
const track = mainLine.querySelector(".bar-track");
if (track) track.style.height = `${effectiveHeight}px`;
const inlineValue = mainLine.querySelector(".value-right");
if (inlineValue) inlineValue.style.height = `${effectiveHeight}px`;
});
}
_applyTopRightValueLayout() {
if (!this.shadowRoot) return;
this.shadowRoot.querySelectorAll(".main-line.left-mode").forEach((mainLine) => {
var _a, _b, _c, _d, _e;
const rowStack = mainLine.closest(".row-stack");
const inlineValue = mainLine.querySelector(".value-right");
const topValue = rowStack == null ? void 0 : rowStack.querySelector(".top-right-value");
if (!rowStack || !inlineValue || !topValue) return;
const active = this._shouldUseTopValueRow(mainLine);
rowStack.dataset.topValue = active ? "true" : "false";
topValue.dataset.active = active ? "true" : "false";
const display = this._decodeDataAttr(inlineValue.dataset.display || "");
const unit = this._decodeDataAttr(inlineValue.dataset.unit || "");
topValue.dataset.display = inlineValue.dataset.display || "";
topValue.dataset.unit = inlineValue.dataset.unit || "";
topValue.dataset.hideUnit = "false";
const availableWidth = (_e = (_d = (_c = (_a = rowStack.getBoundingClientRect) == null ? void 0 : _a.call(rowStack).width) != null ? _c : (_b = topValue.getBoundingClientRect) == null ? void 0 : _b.call(topValue).width) != null ? _d : topValue.clientWidth) != null ? _e : 0;
const fullValueWidth = display ? Math.ceil(this._measureValueMarkupWidth(topValue, display, unit, false) + 2) : 0;
const hideUnit = !!unit && availableWidth > 0 && fullValueWidth > availableWidth;
topValue.dataset.hideUnit = hideUnit ? "true" : "false";
topValue.innerHTML = this._formatRightValueMarkup(display, unit, hideUnit);
});
}
_applyLeftLabelUsefulness() {
if (!this.shadowRoot) return;
this.shadowRoot.querySelectorAll(".main-line.left-mode").forEach((mainLine) => {
var _a;
const labelWrap = mainLine.querySelector(".label-left");
const labelText = mainLine.querySelector(".label-left-text");
if (!labelWrap || !labelText) return;
const row = ((_a = mainLine.closest) == null ? void 0 : _a.call(mainLine, ".row")) || {
dataset: {},
querySelector: (selector) => selector === ".main-line" ? mainLine : selector === ".label-left" ? labelWrap : selector === ".label-left-text" ? labelText : null
};
const metrics = this._getStableLeftLabelMetrics(row);
if (!metrics) return;
const visibleChars = this._measureVisibleLabelCharacters(metrics.labelText, metrics.text, metrics.labelWidth);
labelWrap.dataset.hidden = this._shouldHideLeftLabel(
metrics.text,
metrics.naturalWidth,
metrics.labelWidth,
visibleChars
) ? "true" : "false";
});
}
_runPostLayoutPasses(rows = null) {
const generation = this._rowGeneration;
requestAnimationFrame(() => {
var _a;
if (!this.isConnected || generation !== this._rowGeneration) return;
this._applyRowDensity();
this._applyLeftModeDensity();
this._applyAboveLabelDensity();
this._applyHeroValueFit();
this._applyInsideLabelDensity();
this._applyValueWidthReservation();
const leftWidths = new Map(
[...((_a = this.shadowRoot) == null ? void 0 : _a.querySelectorAll(".main-line.left-mode")) || []].map((mainLine) => [mainLine, mainLine.getBoundingClientRect().width])
);
requestAnimationFrame(() => {
var _a2;
if (!this.isConnected || generation !== this._rowGeneration) return;
this._applyAdaptiveRowHeight();
this._applyValueVisibility();
this._applyLeftLabelUsefulness();
this._applyTopRightValueLayout();
this._ensureMinimumBarShare(rows, leftWidths);
this._applyTopRightValueLayout();
this._applyLeftLabelUsefulness();
const targetRows = rows || ((_a2 = this.shadowRoot) == null ? void 0 : _a2.querySelectorAll(".row[data-entity]")) || [];
targetRows.forEach((row) => {
this._positionTargetLabel(row);
this._positionMarkerValueLabel(row, ".peak-value-label", ".peak-marker");
this._positionMarkerValueLabel(row, ".floor-value-label", ".floor-marker");
this._positionGenericMarkerLabels(row);
});
});
});
}
_isTightUnit(unit) {
return isTightUnit(unit);
}
_encodeDataAttr(value) {
return encodeURIComponent(String(value != null ? value : ""));
}
_decodeDataAttr(value) {
const raw = String(value != null ? value : "");
try {
return decodeURIComponent(raw);
} catch (_err) {
return raw;
}
}
_parseColorToRgb(color) {
const value = String(color || "").trim();
if (!value) return null;
const hexMatch = value.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
if (hexMatch) {
const hex = hexMatch[1];
const full = hex.length === 3 ? hex.split("").map((c) => c + c).join("") : hex;
return {
r: parseInt(full.slice(0, 2), 16),
g: parseInt(full.slice(2, 4), 16),
b: parseInt(full.slice(4, 6), 16)
};
}
const rgbMatch = value.match(/^rgba?\(([^)]+)\)$/i);
if (rgbMatch) {
const parts = rgbMatch[1].split(",").map((p) => p.trim());
if (parts.length >= 3) {
return {
r: Math.max(0, Math.min(255, parseFloat(parts[0]))),
g: Math.max(0, Math.min(255, parseFloat(parts[1]))),
b: Math.max(0, Math.min(255, parseFloat(parts[2])))
};
}
}
return null;
}
_rgbToHsl({ r, g, b }) {
const rn = r / 255;
const gn = g / 255;
const bn = b / 255;
const max = Math.max(rn, gn, bn);
const min = Math.min(rn, gn, bn);
const l = (max + min) / 2;
if (max === min) {
return { h: 0, s: 0, l: l * 100 };
}
const d = max - min;
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
let h;
switch (max) {
case rn:
h = ((gn - bn) / d + (gn < bn ? 6 : 0)) / 6;
break;
case gn:
h = ((bn - rn) / d + 2) / 6;
break;
default:
h = ((rn - gn) / d + 4) / 6;
break;
}
return { h: h * 360, s: s * 100, l: l * 100 };
}
_getMarkerContrastColor(color) {
const rgb = this._parseColorToRgb(color);
if (!rgb) return "#f3f4f6";
const { h, s, l } = this._rgbToHsl(rgb);
const contrastL = Math.abs(l - 90) >= Math.abs(l - 10) ? 90 : 10;
const contrastS = Math.max(40, Math.min(100, s));
return `hsl(${Math.round(h)} ${Math.round(contrastS)}% ${Math.round(contrastL)}%)`;
}
_getEffectiveMarkerColor(marker) {
var _a;
return (_a = marker == null ? void 0 : marker.color) != null ? _a : "#888888";
}
_getMarkerLabelColorStyle(marker) {
const color = this._getEffectiveMarkerColor(marker);
return `--marker-color:${color};--marker-contrast-color:${this._getMarkerContrastColor(color)};`;
}
_getNeedleBorderColor(color) {
const rgb = this._parseColorToRgb(color);
if (!rgb) return "#000000";
const toLinear = (channel) => {
const srgb = channel / 255;
return srgb <= 0.04045 ? srgb / 12.92 : ((srgb + 0.055) / 1.055) ** 2.4;
};
const luminance = 0.2126 * toLinear(rgb.r) + 0.7152 * toLinear(rgb.g) + 0.0722 * toLinear(rgb.b);
return luminance < 0.22 ? "#ffffff" : "#000000";
}
_formatDisplayWithUnit(display, unit) {
return formatDisplayWithUnit(display, unit);
}
_formatRightValueMarkup(display, unit, hideUnit = false) {
const escapedDisplay = escapeHtml(display);
if (!unit || hideUnit) {
return `<span class="value-right-text"><span class="value-right-number">${escapedDisplay}</span></span>`;
}
const cleanUnit = String(unit);
const escapedUnit = escapeHtml(cleanUnit);
const tightUnit = this._isTightUnit(cleanUnit);
const textClass = tightUnit ? "value-right-text tight-unit" : "value-right-text has-unit";
return `<span class="${textClass}"><span class="value-right-number">${escapedDisplay}</span><span class="unit-group"><span class="unit">${escapedUnit}</span></span></span>`;
}
_formatAboveValueMarkup(display, unit, hideUnit = false) {
return `<span class="above-bar-label-value" data-display="${this._encodeDataAttr(display)}" data-unit="${this._encodeDataAttr(unit)}" data-hide-unit="${hideUnit ? "true" : "false"}">${this._formatRightValueMarkup(display, unit, hideUnit)}</span>`;
}
_formatInsideValueMarkup(display, unit, hideUnit = false) {
const escapedDisplay = escapeHtml(display);
if (!unit || hideUnit) return `<span class="inside-value-text"><span class="inside-number">${escapedDisplay}</span></span>`;
const cleanUnit = String(unit);
const escapedUnit = escapeHtml(cleanUnit);
const unitModeClass = this._isTightUnit(cleanUnit) ? "tight-unit" : "has-unit";
return `<span class="inside-value-text ${unitModeClass}"><span class="inside-number">${escapedDisplay}</span><span class="inside-unit">${escapedUnit}</span></span>`;
}
_getRowMarkerModels(rowViewModel, ecfg, peakPct, peakDisplay, targetPct, targetDisplay, peakColor, targetColor) {
if (rowViewModel == null ? void 0 : rowViewModel.markers) {
return rowViewModel.markers.map((marker) => {
var _a, _b;
if (marker.type === "target") {
return {
...marker,
position: targetPct === void 0 ? marker.position : targetPct,
visible: targetPct !== null && targetPct !== void 0,
color: targetColor || marker.color,
label: (_a = marker.label) != null ? _a : targetDisplay === null ? null : { text: targetDisplay }
};
}
if (marker.type === "floor") {
return marker;
}
if (marker.type !== "peak") return marker;
return {
...marker,
position: peakPct === void 0 ? marker.position : peakPct,
visible: peakPct !== null && peakPct !== void 0 && ecfg.peak_marker.show === true,
color: peakColor || marker.color,
label: (_b = marker.label) != null ? _b : peakDisplay === null ? null : { text: peakDisplay }
};
});
}
const markers = buildMarkerModels({
entityConfig: ecfg,
targetPosition: targetPct,
targetPresentation: targetDisplay === null ? null : { text: targetDisplay },
targetVisible: Number.isFinite(targetPct),
peakPosition: peakPct,
peakPresentation: peakDisplay === null ? null : { number: peakDisplay },
peakVisible: Number.isFinite(peakPct)
});
const targetMarker = this._getMarkerModel(markers, "target");
const peakMarker = this._getMarkerModel(markers, "peak");
if (targetMarker && targetColor) targetMarker.color = targetColor;
if (peakMarker && peakColor) peakMarker.color = peakColor;
return markers;
}
_getMarkerModel(markers, type) {
var _a;
return (_a = markers.find((marker) => marker.id === type || marker.type === type)) != null ? _a : null;
}
_renderMarker(marker) {
var _a, _b, _c, _d, _e, _f, _g;
if (!marker) return "";
const position = Number.isFinite(marker.position) ? marker.position : 0;
const color = this._getEffectiveMarkerColor(marker);
const contrastColor = this._getMarkerContrastColor(color);
const display = marker.visible ? "" : "none";
const defaultShape = marker.type === "target" ? "diamond" : marker.type === "generic" ? "circle" : "triangle";
const shape = normalizeMarkerShape(marker.shape, defaultShape);
const lane = (_a = marker.lane) != null ? _a : marker.type === "peak" ? "above" : "below";
const shapePaths = `<g class="marker-shape-paths">
<path data-shape="circle" d="M8 1A7 7 0 1 0 8 15A7 7 0 1 0 8 1Z"></path>
<path data-shape="diamond" d="M8 1L15 8L8 15L1 8Z"></path>
<path data-shape="chevron" d="M2 2L8 8L14 2 M2 8L8 14L14 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
<path data-shape="arrow" d="M8 15 L3 3 H6 L8 7 L10 3 H13 Z"></path>
<path data-shape="pin" fill-rule="evenodd" d="M8 15.5 C7.1 14 3 9.7 3 6 A5 5 0 1 1 13 6 C13 9.7 8.9 14 8 15.5 Z M8 4.2 A1.8 1.8 0 1 0 8 7.8 A1.8 1.8 0 1 0 8 4.2 Z"></path>
</g>`;
if (marker.type === "generic") {
const triangleClasses = lane === "above" ? ["peak-inset", "peak-outset"] : ["target-inset", "target-outset"];
return `
<div class="generic-marker" data-marker-id="${escapeHtml(marker.id)}" data-shape="${shape}" data-lane="${lane}" data-direction="${(_b = marker.direction) != null ? _b : "inward"}" data-show-marker="${marker.showMarker === false ? "false" : "true"}" style="left:${position}%;--marker-color:${color};--marker-contrast-color:${contrastColor};display:${display};">
<div class="${triangleClasses[0]}"></div>
<div class="${triangleClasses[1]}"></div>
<svg class="marker-shape-svg" data-shape="${shape}" data-lane="${lane}" data-direction="${(_c = marker.direction) != null ? _c : "inward"}" viewBox="0 0 16 16" aria-hidden="true" focusable="false">${shapePaths}</svg>
</div>`;
}
if (marker.type === "target" || marker.type === "floor") {
const markerClass = `${marker.type}-marker`;
return `
<div class="${markerClass}" data-shape="${shape}" data-lane="${lane}" data-direction="${(_d = marker.direction) != null ? _d : "inward"}" style="left:${position}%;--marker-color:${color};--marker-contrast-color:${contrastColor};display:${display};">
<div class="${marker.type}-inset"></div>
<div class="${marker.type}-outset"></div>
<svg class="marker-shape-svg" data-shape="${shape}" data-lane="${lane}" data-direction="${(_e = marker.direction) != null ? _e : "inward"}" viewBox="0 0 16 16" aria-hidden="true" focusable="false">${shapePaths}</svg>
</div>`;
}
return `
<div class="peak-marker" data-shape="${shape}" data-lane="${lane}" data-direction="${(_f = marker.direction) != null ? _f : "inward"}" style="left:${position}%;--marker-color:${color};--marker-contrast-color:${contrastColor};display:${display};">
<div class="peak-outset"></div>
<div class="peak-inset"></div>
<svg class="marker-shape-svg" data-shape="${shape}" data-lane="${lane}" data-direction="${(_g = marker.direction) != null ? _g : "inward"}" viewBox="0 0 16 16" aria-hidden="true" focusable="false">${shapePaths}</svg>
</div>`;
}
_patchMarker(markerEl, marker) {
var _a, _b, _c, _d, _e;
if (!markerEl || !marker) return;
if (!marker.visible) this._clearMarkerHover(markerEl);
const defaultShape = marker.type === "generic" ? "circle" : marker.type === "peak" || marker.type === "floor" ? "triangle" : "diamond";
const shape = normalizeMarkerShape(marker.shape, defaultShape);
this._setDatasetIfChanged(markerEl, "shape", shape);
this._setDatasetIfChanged(markerEl, "lane", (_a = marker.lane) != null ? _a : marker.type === "peak" ? "above" : "below");
this._setDatasetIfChanged(markerEl, "direction", (_b = marker.direction) != null ? _b : "inward");
if (marker.type === "generic") {
this._setDatasetIfChanged(markerEl, "showMarker", marker.showMarker === false ? "false" : "true");
}
const shapeSvg = (_c = markerEl.querySelector) == null ? void 0 : _c.call(markerEl, ".marker-shape-svg");
if (shapeSvg) {
this._setDatasetIfChanged(shapeSvg, "shape", shape);
this._setDatasetIfChanged(shapeSvg, "lane", (_d = marker.lane) != null ? _d : marker.type === "peak" ? "above" : "below");
this._setDatasetIfChanged(shapeSvg, "direction", (_e = marker.direction) != null ? _e : "inward");
}
this._setStyleIfChanged(markerEl, "display", marker.visible ? "" : "none");
if (marker.visible && Number.isFinite(marker.position)) {
this._setStyleIfChanged(markerEl, "left", `${marker.position}%`);
}
const markerColor = this._getEffectiveMarkerColor(marker);
this._setStyleIfChanged(markerEl, "--marker-color", markerColor);
this._setStyleIfChanged(markerEl, "--marker-contrast-color", this._getMarkerContrastColor(markerColor));
}
_patchMarkerLabelAppearance(labelEl, marker) {
if (!labelEl || !marker) return;
const markerColor = this._getEffectiveMarkerColor(marker);
this._setStyleIfChanged(labelEl, "--marker-color", markerColor);
this._setStyleIfChanged(labelEl, "--marker-contrast-color", this._getMarkerContrastColor(markerColor));
}
_buildRowViewModel(entityCfg, ecfg, stateObj) {
var _a;
const rowViewModel = buildRowViewModel({
hass: this._hass,
cardConfig: this._config,
entityConfig: ecfg,
entityState: stateObj,
extrema: (_a = this._extrema.get(entityCfg)) != null ? _a : null,
previousScale: this._rowScales.get(entityCfg)
});
this._rowScales.set(entityCfg, { min: rowViewModel.min, max: rowViewModel.max });
return rowViewModel;
}
_buildRow(entityCfg, stateDisplay, unit, pct, color, peakPct, peakDisplay, targetPct, targetDisplay, peakColor, targetColor, minValue, maxValue, rowIndex = ((_b) => (_b = ((_a) => (_a = this._config.entities) == null ? void 0 : _a.indexOf(entityCfg))()) != null ? _b : -1)()) {
var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E;
const ecfg = this._resolve(entityCfg);
const stateObj = (_c = (_b2 = (_a2 = this._hass) == null ? void 0 : _a2.states) == null ? void 0 : _b2[entityCfg.entity]) != null ? _c : null;
if (stateObj) this._updateExtrema(entityCfg, ecfg, stateObj);
const rowViewModel = stateObj ? this._buildRowViewModel(entityCfg, ecfg, stateObj) : null;
const layout = ecfg.layout;
const bar = ecfg.bar;
const safeMin = Number.isFinite(minValue) ? minValue : 0;
const safeMax = Number.isFinite(maxValue) ? maxValue : 100;
const baselinePct = (_d = rowViewModel == null ? void 0 : rowViewModel.baselinePercent) != null ? _d : this._resolveBaselinePct(ecfg, safeMin, safeMax);
const lp = layout.label.position;
const h = (_f = (_e = rowViewModel == null ? void 0 : rowViewModel.attributes) == null ? void 0 : _e.baseHeight) != null ? _f : layout.height;
const name = (_j = (_i = (_g = rowViewModel == null ? void 0 : rowViewModel.name) != null ? _g : ecfg.name) != null ? _i : (_h = stateObj == null ? void 0 : stateObj.attributes) == null ? void 0 : _h.friendly_name) != null ? _j : entityCfg.entity;
const escapedEntityId = escapeHtml((_k = rowViewModel == null ? void 0 : rowViewModel.entityId) != null ? _k : entityCfg.entity);
const escapedName = escapeHtml(name);
const markerModels = this._getRowMarkerModels(rowViewModel, ecfg, peakPct, peakDisplay, targetPct, targetDisplay, peakColor, targetColor);
const targetMarkerModel = this._getMarkerModel(markerModels, "target");
const peakMarkerModel = this._getMarkerModel(markerModels, "peak");
const floorMarkerModel = this._getMarkerModel(markerModels, "floor");
const genericMarkerModels = markerModels.filter((marker) => marker.type === "generic");
const markerLaneOccupancy = (_l = rowViewModel == null ? void 0 : rowViewModel.markerLaneOccupancy) != null ? _l : getMarkerLaneOccupancy(ecfg);
const markerLabelLaneOccupancy = (_m = rowViewModel == null ? void 0 : rowViewModel.markerLabelLaneOccupancy) != null ? _m : getMarkerLabelLaneOccupancy(ecfg);
const rawValue = (_n = rowViewModel == null ? void 0 : rowViewModel.numericValue) != null ? _n : this._getFiniteNumber(stateDisplay);
const needleState = (_o = rowViewModel == null ? void 0 : rowViewModel.needle) != null ? _o : this._getNeedleRenderState(rawValue, ecfg, safeMin, safeMax, baselinePct);
const fillState = this._getFillRenderState(pct, "var(--sbcp-row-height)", ecfg, color, targetPct, baselinePct, safeMin, safeMax, needleState.show);
const baselineAt = (_p = ecfg.baseline) == null ? void 0 : _p.at;
const baselineConfigured = ((_q = ecfg.baseline) == null ? void 0 : _q.enabled) !== false && (Number.isFinite(baselinePct) || Boolean(baselineAt == null ? void 0 : baselineAt.entity) || (baselineAt == null ? void 0 : baselineAt.fixed) !== null && (baselineAt == null ? void 0 : baselineAt.fixed) !== void 0 || Number.isFinite(baselineAt == null ? void 0 : baselineAt.percent));
const baselineIndicator = baselineConfigured ? `<div class="baseline-indicator" aria-hidden="true" style="${Number.isFinite(baselinePct) ? `left:${baselinePct}%;display:block;` : "display:none;"}"></div>` : "";
const peakMarker = this._renderMarker(peakMarkerModel);
const targetMarker = this._renderMarker(targetMarkerModel);
const floorMarker = this._renderMarker(floorMarkerModel);
const targetValueLabel = (targetMarkerModel == null ? void 0 : targetMarkerModel.labelVisible) ? `
<div class="target-value-label" style="left:${Number.isFinite(targetMarkerModel.position) ? targetMarkerModel.position : 0}%;visibility:${targetMarkerModel.visible && ((_r = targetMarkerModel.label) == null ? void 0 : _r.text) ? "visible" : "hidden"};${this._getMarkerLabelColorStyle(targetMarkerModel)}">
${((_s = targetMarkerModel.label) == null ? void 0 : _s.text) ? escapeHtml(targetMarkerModel.label.text) : ""}
</div>` : "";
const peakValueLabel = (peakMarkerModel == null ? void 0 : peakMarkerModel.labelVisible) ? `
<div class="peak-value-label" style="left:${Number.isFinite(peakMarkerModel.position) ? peakMarkerModel.position : 0}%;visibility:${peakMarkerModel.visible && ((_t = peakMarkerModel.label) == null ? void 0 : _t.text) ? "visible" : "hidden"};${this._getMarkerLabelColorStyle(peakMarkerModel)}">
${peakMarkerModel.visible && ((_u = peakMarkerModel.label) == null ? void 0 : _u.text) ? escapeHtml(peakMarkerModel.label.text) : ""}
</div>` : "";
const floorValueLabel = (floorMarkerModel == null ? void 0 : floorMarkerModel.labelVisible) ? `
<div class="floor-value-label" style="left:${Number.isFinite(floorMarkerModel.position) ? floorMarkerModel.position : 0}%;visibility:${floorMarkerModel.visible && ((_v = floorMarkerModel.label) == null ? void 0 : _v.text) ? "visible" : "hidden"};${this._getMarkerLabelColorStyle(floorMarkerModel)}">
${floorMarkerModel.visible && ((_w = floorMarkerModel.label) == null ? void 0 : _w.text) ? escapeHtml(floorMarkerModel.label.text) : ""}
</div>` : "";
const genericMarkers = genericMarkerModels.map((marker) => this._renderMarker(marker)).join("");
const genericValueLabels = genericMarkerModels.filter((marker) => marker.labelVisible).map((marker) => {
var _a3, _b3;
return `
<div class="generic-value-label" data-marker-id="${escapeHtml(marker.id)}" data-lane="${marker.lane}" data-show-marker="${marker.showMarker === false ? "false" : "true"}" style="left:${Number.isFinite(marker.position) ? marker.position : 0}%;visibility:${marker.visible && ((_a3 = marker.label) == null ? void 0 : _a3.text) ? "visible" : "hidden"};${this._getMarkerLabelColorStyle(marker)}">
${marker.visible && ((_b3 = marker.label) == null ? void 0 : _b3.text) ? escapeHtml(marker.label.text) : ""}
</div>`;
}).join("");
const needleMarker = ((_y = (_x = ecfg.bar) == null ? void 0 : _x.needle) == null ? void 0 : _y.show) && !Number.isFinite(baselinePct) ? `
<div class="needle-layer">
<div class="needle-marker" data-edge="${needleState.edge}" style="left:${(_z = needleState.pct) != null ? _z : 0}%;--needle-color:${needleState.color};--needle-border-color:${needleState.borderColor};display:${needleState.show ? "block" : "none"};"></div>
</div>` : "";
const paintLayers = fillState.paintLayers.map((layer) => `
<div class="bar-paint-layer" data-layer="${layer.id}" style="z-index:${layer.zIndex};${layer.paintStyle}${layer.revealStyle}"></div>`).join("");
const aboveLabel = lp === "above" ? `
<div class="above-line">
${ecfg.icon && ecfg.icon !== false ? `<div class="above-icon-spacer"></div>` : ""}
<div class="above-bar-label">
<span class="above-bar-label-name label-left-text">${escapedName}</span>
${this._formatAboveValueMarkup(stateDisplay, unit, false)}
</div>
</div>` : "";
const heroSize = (_A = layout.hero.size) != null ? _A : "small";
const heroFontSize = layout.hero.value_size;
const heroHeader = lp === "hero" ? `
<div class="hero-line" data-hero-size="${heroSize}"${Number.isFinite(heroFontSize) ? ` style="--sbcp-hero-base-size:${heroFontSize}px"` : ""}>
<div class="hero-header">
<span class="hero-label label-left-text">${escapedName}</span>
<span class="hero-value" data-display="${this._encodeDataAttr(stateDisplay)}" data-unit="${this._encodeDataAttr(unit)}">${this._formatRightValueMarkup(stateDisplay, unit, false)}</span>
</div>
</div>` : "";
const innerLabel = lp === "inside" ? `
<div class="bar-inner-label">
<span class="inside-name">${escapedName}</span>
<span class="inside-value" data-display="${this._encodeDataAttr(stateDisplay)}" data-unit="${this._encodeDataAttr(unit)}" data-hide-unit="false" data-hide-value="false">${this._formatInsideValueMarkup(stateDisplay, unit, false)}</span>
</div>` : "";
const leftLabel = lp === "left" ? `<div class="label-left" style="flex:0 1 min(${layout.label.width}px, var(--sbcp-left-label-share));max-width:min(${layout.label.width}px, var(--sbcp-left-label-share));"><span class="label-left-text">${escapedName}</span></div>` : "";
const rightValue = lp !== "inside" && lp !== "above" && lp !== "hero" ? `<div class="value-right" data-display="${this._encodeDataAttr(stateDisplay)}" data-unit="${this._encodeDataAttr(unit)}" data-hide-unit="false">${this._formatRightValueMarkup(stateDisplay, unit, false)}</div>` : "";
const topRightValue = lp === "left" ? `<div class="top-right-value" data-display="${this._encodeDataAttr(stateDisplay)}" data-unit="${this._encodeDataAttr(unit)}" data-hide-unit="false" data-active="false">${this._formatRightValueMarkup(stateDisplay, unit, false)}</div>` : "";
const escapedIcon = ecfg.icon && ecfg.icon !== false ? escapeHtml(ecfg.icon) : "";
const mainIcon = escapedIcon && lp !== "hero" ? `<div class="icon-wrap"><ha-icon icon="${escapedIcon}"></ha-icon></div>` : "";
return `
<div class="row" data-row-index="${rowIndex}" data-entity="${escapedEntityId}" data-base-height="${h}" data-height-explicit="${((_C = (_B = rowViewModel == null ? void 0 : rowViewModel.attributes) == null ? void 0 : _B.heightExplicit) != null ? _C : layout.height_explicit) ? "true" : "false"}" data-bar-animated="${((_E = (_D = rowViewModel == null ? void 0 : rowViewModel.attributes) == null ? void 0 : _D.barAnimated) != null ? _E : bar.animated) ? "true" : "false"}" data-marker-label-lane-above="${markerLabelLaneOccupancy.above ? "true" : "false"}" data-marker-label-lane-below="${markerLabelLaneOccupancy.below ? "true" : "false"}">
<div class="row-stack" style="--sbcp-row-height:${h}px;">
${aboveLabel}
${heroHeader}
${topRightValue}
<div class="main-line ${lp}-mode" data-marker-lane-above="${markerLaneOccupancy.above ? "true" : "false"}" data-marker-lane-below="${markerLaneOccupancy.below ? "true" : "false"}" style="height:${h}px;">
${mainIcon}
${leftLabel}
<div class="bar-wrap">
<div class="bar-track">
<div class="bar-fill-reveal${bar.animated ? "" : " no-anim"}" style="${fillState.revealStyle}">
${paintLayers}
</div>
${baselineIndicator}
${innerLabel}
${peakMarker}
${targetMarker}
${floorMarker}
${genericMarkers}
${needleMarker}
</div>
${peakValueLabel}
${targetValueLabel}
${floorValueLabel}
${genericValueLabels}
</div>
${rightValue}
</div>
</div>
</div>`;
}
_patchRow(row, entityCfg, stateObj, previousHass = null) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
if (!row || !stateObj) return;
const ecfg = this._resolve(entityCfg);
this._updateExtrema(entityCfg, ecfg, stateObj);
const previousScale = this._rowScales.get(entityCfg);
const rowViewModel = this._buildRowViewModel(entityCfg, ecfg, stateObj);
const safeMin = rowViewModel.min;
const safeMax = rowViewModel.max;
const pct = rowViewModel.percent;
const color = this._getColor(pct, ecfg, safeMin, safeMax);
const display = rowViewModel.primaryPresentation.number;
const displayUnit = rowViewModel.primaryPresentation.unit;
const fillReveal = row.querySelector(".bar-fill-reveal");
const baselineIndicator = row.querySelector(".baseline-indicator");
const paintLayer = row.querySelector('.bar-paint-layer[data-layer="base"]');
const liveTargetPct = rowViewModel.targetPercent;
const liveBaselinePct = rowViewModel.baselinePercent;
const needleState = rowViewModel.needle;
const fillState = this._getFillRenderState(pct, "var(--sbcp-row-height)", ecfg, color, liveTargetPct, liveBaselinePct, safeMin, safeMax, needleState.show);
const previousStateObj = (_b = (_a = previousHass == null ? void 0 : previousHass.states) == null ? void 0 : _a[entityCfg.entity]) != null ? _b : null;
const previousViewModel = previousStateObj ? buildRowViewModel({
hass: previousHass,
cardConfig: this._config,
entityConfig: ecfg,
entityState: previousStateObj,
extrema: (_c = this._extrema.get(entityCfg)) != null ? _c : null,
previousScale
}) : null;
const revealDuration = this._getRevealTransitionDuration(
previousViewModel && Number.isFinite(previousViewModel.numericValue) ? { valuePercent: previousViewModel.percent, baselinePercent: previousViewModel.baselinePercent } : null,
Number.isFinite(rowViewModel.numericValue) ? { valuePercent: pct, baselinePercent: liveBaselinePct } : null
);
if (fillReveal) {
this._setStyleTextIfChanged(fillReveal, `${fillState.revealStyle};--sbcp-reveal-duration:${revealDuration}ms`);
this._setClassNameIfChanged(fillReveal, `bar-fill-reveal${ecfg.bar.animated ? "" : " no-anim"}`);
}
if (baselineIndicator) {
this._setStyleIfChanged(baselineIndicator, "display", Number.isFinite(liveBaselinePct) ? "block" : "none");
if (Number.isFinite(liveBaselinePct)) {
this._setStyleIfChanged(baselineIndicator, "left", `${liveBaselinePct}%`);
}
}
if (paintLayer) {
const baseLayerState = fillState.paintLayers.find((layer) => layer.id === "base");
if (baseLayerState) {
this._setStyleTextIfChanged(paintLayer, `z-index:${baseLayerState.zIndex};${baseLayerState.paintStyle}${baseLayerState.revealStyle}`);
}
}
const aboveTargetLayer = row.querySelector('.bar-paint-layer[data-layer="above-target"]');
if (aboveTargetLayer) {
const aboveTargetState = fillState.paintLayers.find((layer) => layer.id === "above-target");
if (aboveTargetState) {
this._setStyleTextIfChanged(aboveTargetLayer, `z-index:${aboveTargetState.zIndex};${aboveTargetState.paintStyle}${aboveTargetState.revealStyle}`);
}
}
const needleEl = row.querySelector(".needle-marker");
if (needleEl) {
this._setStyleIfChanged(needleEl, "display", needleState.show ? "block" : "none");
this._setStyleIfChanged(needleEl, "left", `${(_d = needleState.pct) != null ? _d : 0}%`);
this._setStyleIfChanged(needleEl, "--needle-color", needleState.color);
this._setStyleIfChanged(needleEl, "--needle-border-color", needleState.borderColor);
this._setDatasetIfChanged(needleEl, "edge", needleState.edge);
}
this._setDatasetIfChanged(row, "baseHeight", rowViewModel.attributes.baseHeight);
this._setDatasetIfChanged(row, "heightExplicit", rowViewModel.attributes.heightExplicit ? "true" : "false");
this._setDatasetIfChanged(row, "barAnimated", rowViewModel.attributes.barAnimated ? "true" : "false");
const valueEl = row.querySelector(".value-right");
if (valueEl) {
valueEl.dataset.display = this._encodeDataAttr(display);
valueEl.dataset.unit = this._encodeDataAttr(displayUnit);
valueEl.dataset.hideUnit = "false";
valueEl.innerHTML = this._formatRightValueMarkup(display, displayUnit, false);
}
const topValueEl = row.querySelector(".top-right-value");
if (topValueEl) {
topValueEl.dataset.display = this._encodeDataAttr(display);
topValueEl.dataset.unit = this._encodeDataAttr(displayUnit);
topValueEl.dataset.hideUnit = "false";
topValueEl.innerHTML = this._formatRightValueMarkup(display, displayUnit, false);
}
const innerLabel = row.querySelector(".bar-inner-label");
if (innerLabel) {
const valueSpan = innerLabel.querySelector(".inside-value");
if (valueSpan) {
valueSpan.dataset.display = this._encodeDataAttr(display);
valueSpan.dataset.unit = this._encodeDataAttr(displayUnit);
valueSpan.dataset.hideUnit = "false";
valueSpan.dataset.hideValue = "false";
valueSpan.innerHTML = this._formatInsideValueMarkup(display, displayUnit, false);
}
}
const heroHeader = row.querySelector(".hero-header");
if (heroHeader) {
const heroLine = row.querySelector(".hero-line");
this._setStyleIfChanged(
heroLine,
"--sbcp-hero-base-size",
Number.isFinite(ecfg.layout.hero.value_size) ? `${ecfg.layout.hero.value_size}px` : null
);
heroHeader.innerHTML = `<span class="hero-label label-left-text">${escapeHtml(rowViewModel.name)}</span><span class="hero-value" data-display="${this._encodeDataAttr(display)}" data-unit="${this._encodeDataAttr(displayUnit)}">${this._formatRightValueMarkup(display, displayUnit, false)}</span>`;
}
const aboveLabel = heroHeader ? null : row.querySelector(".above-bar-label");
if (aboveLabel) {
aboveLabel.innerHTML = `<span class="above-bar-label-name label-left-text">${escapeHtml(rowViewModel.name)}</span>${this._formatAboveValueMarkup(display, displayUnit, false)}`;
}
const targetEl = row.querySelector(".target-marker");
const targetLabelEl = row.querySelector(".target-value-label");
const peakLabelEl = row.querySelector(".peak-value-label");
const floorLabelEl = row.querySelector(".floor-value-label");
const markerModels = (_e = rowViewModel.markers) != null ? _e : [];
this._patchMarker(targetEl, this._getMarkerModel(markerModels, "target"));
this._patchMarker(row.querySelector(".peak-marker"), this._getMarkerModel(markerModels, "peak"));
this._patchMarker(row.querySelector(".floor-marker"), this._getMarkerModel(markerModels, "floor"));
((_g = (_f = row.querySelectorAll) == null ? void 0 : _f.call(row, ".generic-marker[data-marker-id]")) != null ? _g : []).forEach((markerEl) => {
var _a2, _b2, _c2, _d2, _e2;
const markerId = markerEl.dataset.markerId;
const marker = this._getMarkerModel(markerModels, markerId);
this._patchMarker(markerEl, marker);
const labelEl = [...(_b2 = (_a2 = row.querySelectorAll) == null ? void 0 : _a2.call(row, ".generic-value-label[data-marker-id]")) != null ? _b2 : []].find((label) => label.dataset.markerId === markerId);
if (!labelEl) return;
this._setDatasetIfChanged(labelEl, "showMarker", (marker == null ? void 0 : marker.showMarker) === false ? "false" : "true");
this._patchMarkerLabelAppearance(labelEl, marker);
if ((marker == null ? void 0 : marker.labelVisible) && marker.visible && ((_c2 = marker.label) == null ? void 0 : _c2.text)) {
this._setTextIfChanged(labelEl, (_e2 = (_d2 = marker.label) == null ? void 0 : _d2.text) != null ? _e2 : null);
this._setStyleIfChanged(labelEl, "visibility", "visible");
this._setStyleIfChanged(labelEl, "left", `${Number.isFinite(marker.position) ? marker.position : 0}%`);
} else {
this._setStyleIfChanged(labelEl, "visibility", "hidden");
}
});
const targetMarkerModel = this._getMarkerModel(markerModels, "target");
this._patchMarkerLabelAppearance(targetLabelEl, targetMarkerModel);
if (targetLabelEl) {
if ((targetMarkerModel == null ? void 0 : targetMarkerModel.labelVisible) && targetMarkerModel.visible && ((_h = targetMarkerModel.label) == null ? void 0 : _h.text)) {
this._setTextIfChanged(targetLabelEl, (_j = (_i = targetMarkerModel.label) == null ? void 0 : _i.text) != null ? _j : null);
} else {
this._setStyleIfChanged(targetLabelEl, "visibility", "hidden");
}
}
const patchValueLabel = (labelEl, markerType) => {
var _a2, _b2, _c2;
if (!labelEl) return;
const marker = this._getMarkerModel(markerModels, markerType);
this._patchMarkerLabelAppearance(labelEl, marker);
if ((marker == null ? void 0 : marker.labelVisible) && marker.visible && ((_a2 = marker.label) == null ? void 0 : _a2.text)) {
this._setTextIfChanged(labelEl, (_c2 = (_b2 = marker.label) == null ? void 0 : _b2.text) != null ? _c2 : null);
this._setStyleIfChanged(labelEl, "visibility", "visible");
this._setStyleIfChanged(labelEl, "left", `${Number.isFinite(marker.position) ? marker.position : 0}%`);
} else {
this._setStyleIfChanged(labelEl, "visibility", "hidden");
}
};
patchValueLabel(peakLabelEl, "peak");
patchValueLabel(floorLabelEl, "floor");
}
_update(previousHass = null) {
var _a, _b, _c, _d;
if (!this._hass || !this._config) return;
const rowsEl = this.shadowRoot.querySelector(".rows");
if (!rowsEl) return;
const entities = this._config.entities;
const presence = entities.map((entityCfg) => !!this._hass.states[entityCfg.entity]);
const presenceChanged = presence.some((present, index) => present !== this._rowPresence[index]);
if (!this._rendered || presenceChanged) {
this._rowGeneration += 1;
this._rowPresence = presence;
let html = "";
for (let entityIndex = 0; entityIndex < entities.length; entityIndex++) {
const entityCfg = entities[entityIndex];
const stateObj = this._hass.states[entityCfg.entity];
if (!stateObj) {
html += `<div class="row" data-row-index="${entityIndex}"><span style="color:var(--error-color,red);font-size:12px;">Entity not found: ${escapeHtml(entityCfg.entity)}</span></div>`;
continue;
}
const ecfg = this._resolve(entityCfg);
this._updateExtrema(entityCfg, ecfg, stateObj);
const rowViewModel = this._buildRowViewModel(entityCfg, ecfg, stateObj);
const safeMin = rowViewModel.min;
const safeMax = rowViewModel.max;
const pct = rowViewModel.percent;
const color = this._getColor(pct, ecfg, safeMin, safeMax);
const display = rowViewModel.primaryPresentation.number;
const displayUnit = rowViewModel.primaryPresentation.unit;
const targetPct = rowViewModel.targetPercent;
const targetDisplay = (_b = (_a = rowViewModel.targetPresentation) == null ? void 0 : _a.text) != null ? _b : null;
const peakPct = rowViewModel.peakPercent;
const peakDisplay = (_d = (_c = rowViewModel.peakPresentation) == null ? void 0 : _c.number) != null ? _d : null;
html += this._buildRow(entityCfg, display, displayUnit, pct, color, peakPct, peakDisplay, targetPct, targetDisplay, ecfg.peak_marker.color, ecfg.target_marker.color, safeMin, safeMax, entityIndex);
}
this._clearMarkerHover();
rowsEl.innerHTML = html;
this._rendered = true;
const builtRows = rowsEl.querySelectorAll(".row[data-entity]");
builtRows.forEach((row) => {
const entityCfg = entities[Number(row.dataset.rowIndex)];
const stateObj = entityCfg ? this._hass.states[entityCfg.entity] : null;
if (entityCfg && stateObj) {
this._patchRow(row, entityCfg, stateObj);
}
});
this._runPostLayoutPasses(builtRows);
builtRows.forEach((row) => {
row.addEventListener("click", () => {
const entityId = row.dataset.entity;
const event = new CustomEvent("hass-more-info", { composed: true, detail: { entityId } });
this.dispatchEvent(event);
});
});
return;
}
const rows = rowsEl.querySelectorAll(".row[data-entity]");
for (const row of rows) {
const entityCfg = entities[Number(row.dataset.rowIndex)];
if (!entityCfg) continue;
const stateObj = this._hass.states[entityCfg.entity];
if (!stateObj) continue;
this._patchRow(row, entityCfg, stateObj, previousHass);
}
this._runPostLayoutPasses(rows);
}
};
}
});
// src/editor/SensorBarCardPlusEditor.js
var SensorBarCardPlusEditor;
var init_SensorBarCardPlusEditor = __esm({
"src/editor/SensorBarCardPlusEditor.js"() {
init_normalize();
init_resolve();
SensorBarCardPlusEditor = class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this._config = {};
this._draftConfig = {};
this._hass = null;
this._isRendering = false;
this._renderScheduled = false;
this._lastRenderedConfigJson = null;
this._lastEmittedConfigJson = null;
this._shadowListenersAttached = false;
this._expandedEntityOverrides = /* @__PURE__ */ new Set();
this._expandedOverrideGroups = /* @__PURE__ */ new Set();
this._expandedCardGroups = /* @__PURE__ */ new Set();
this._genericMarkerUiIds = /* @__PURE__ */ new Map();
this._expandedGenericMarkerUiIds = /* @__PURE__ */ new Set();
this._nextGenericMarkerUiId = 0;
this._gradientStopsDrafts = /* @__PURE__ */ new Map();
this._gradientStopsUiRows = /* @__PURE__ */ new Map();
this._gradientStopPosTexts = /* @__PURE__ */ new Map();
this._gradientStopValidationMessages = /* @__PURE__ */ new Map();
this._segmentDrafts = /* @__PURE__ */ new Map();
this._segmentUiRows = /* @__PURE__ */ new Map();
this._segmentBoundaryTexts = /* @__PURE__ */ new Map();
this._targetAboveFillDrafts = /* @__PURE__ */ new Map();
this._baselineColorDrafts = /* @__PURE__ */ new Map();
this._pendingFocusSelector = null;
this._boundHandleClick = (event) => this._handleClick(event);
this._boundHandleChange = (event) => this._handleChange(event);
this._boundHandleInput = (event) => this._handleInput(event);
this._boundHandleValueChanged = (event) => this._handleValueChanged(event);
this._boundHandleKeydown = (event) => this._handleKeydown(event);
}
setConfig(config) {
var _a;
const nextConfig = this._cloneDeep(config != null ? config : {});
const nextConfigJson = this._serializeConfig(nextConfig);
const currentConfigJson = this._serializeConfig(this._config);
const currentDraftJson = this._serializeConfig(this._draftConfig);
if (nextConfigJson === currentConfigJson) {
return;
}
if (nextConfigJson === currentDraftJson) {
this._config = nextConfig;
return;
}
if (nextConfigJson === this._lastEmittedConfigJson) {
this._config = nextConfig;
return;
}
if (this._getEmittedConfigJson(nextConfig) === this._getEmittedConfigJson(this._draftConfig)) {
this._config = nextConfig;
return;
}
const shouldRender = !((_a = this.shadowRoot) == null ? void 0 : _a.innerHTML) || nextConfigJson !== this._lastRenderedConfigJson;
this._lastEmittedConfigJson = null;
this._config = nextConfig;
this._draftConfig = this._cloneDeep(nextConfig);
this._gradientStopsDrafts = /* @__PURE__ */ new Map();
this._gradientStopsUiRows = /* @__PURE__ */ new Map();
this._gradientStopPosTexts = /* @__PURE__ */ new Map();
this._gradientStopValidationMessages = /* @__PURE__ */ new Map();
this._segmentDrafts = /* @__PURE__ */ new Map();
this._segmentUiRows = /* @__PURE__ */ new Map();
this._segmentBoundaryTexts = /* @__PURE__ */ new Map();
this._targetAboveFillDrafts = /* @__PURE__ */ new Map();
this._baselineColorDrafts = /* @__PURE__ */ new Map();
this._genericMarkerUiIds.clear();
this._expandedGenericMarkerUiIds.clear();
if (shouldRender) {
this._render();
}
}
set hass(hass) {
var _a;
this._hass = hass;
if (!((_a = this.shadowRoot) == null ? void 0 : _a.innerHTML)) {
this._render();
return;
}
this._syncEntityPickers();
}
_cloneContainer(value) {
return Array.isArray(value) ? [...value] : { ...value != null ? value : {} };
}
_cloneDeep(value) {
if (Array.isArray(value)) {
return value.map((entry) => this._cloneDeep(entry));
}
if (this._isObject(value)) {
const clone = {};
for (const [key, entry] of Object.entries(value)) {
clone[key] = this._cloneDeep(entry);
}
return clone;
}
return value;
}
_serializeConfig(value) {
const normalize = (input) => {
if (Array.isArray(input)) {
return input.map((entry) => normalize(entry));
}
if (this._isObject(input)) {
return Object.keys(input).sort().reduce((acc, key) => {
acc[key] = normalize(input[key]);
return acc;
}, {});
}
return input;
};
return JSON.stringify(normalize(value != null ? value : null));
}
_getEmittedConfigJson(config) {
return this._serializeConfig(config);
}
_isObject(value) {
return !!value && typeof value === "object" && !Array.isArray(value);
}
_setPathValue(target, path, value) {
if (!path.length) {
return value;
}
const root = this._cloneContainer(target != null ? target : {});
let cursor = root;
let sourceCursor = target;
for (let index = 0; index < path.length - 1; index++) {
const key = path[index];
const nextSource = this._isObject(sourceCursor == null ? void 0 : sourceCursor[key]) || Array.isArray(sourceCursor == null ? void 0 : sourceCursor[key]) ? sourceCursor[key] : {};
cursor[key] = this._cloneContainer(nextSource);
cursor = cursor[key];
sourceCursor = nextSource;
}
cursor[path[path.length - 1]] = value;
return root;
}
_deletePathValue(target, path) {
if (!path.length || !this._isObject(target)) {
return target;
}
const [key, ...rest] = path;
if (!(key in target)) {
return target;
}
const cloned = this._cloneContainer(target);
if (!rest.length) {
delete cloned[key];
return cloned;
}
const nextValue = this._deletePathValue(cloned[key], rest);
if (nextValue === cloned[key]) {
return target;
}
if (this._isObject(nextValue) && !Object.keys(nextValue).length) {
delete cloned[key];
return cloned;
}
cloned[key] = nextValue;
return cloned;
}
_getPathValue(target, path) {
let cursor = target;
for (const key of path) {
if (cursor == null) return void 0;
cursor = cursor[key];
}
return cursor;
}
_hasPath(target, path) {
let cursor = target;
for (const key of path) {
if (!this._isObject(cursor) && !Array.isArray(cursor)) return false;
if (!(key in cursor)) return false;
cursor = cursor[key];
}
return true;
}
_normalizeTextValue(value) {
return typeof value === "string" ? value : value == null ? "" : String(value);
}
_normalizeOptionalEnabled(value) {
return value === true ? true : value === false ? false : null;
}
_normalizeNumberValue(value) {
if (value === "" || value === null || value === void 0) {
return null;
}
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : null;
}
_normalizeDecimalValue(value) {
if (value === "" || value === null || value === void 0) {
return null;
}
const parsed = Number(value);
if (!Number.isFinite(parsed) || parsed < 0 || !Number.isInteger(parsed)) {
return null;
}
return parsed;
}
_preferStructuredPath(structuredPath, legacyPath = null) {
if (this._hasPath(this._draftConfig, structuredPath.slice(0, -1))) {
return structuredPath;
}
if (this._hasPath(this._draftConfig, structuredPath)) {
return structuredPath;
}
if (legacyPath && this._hasPath(this._draftConfig, legacyPath)) {
return legacyPath;
}
return structuredPath;
}
_getEntitiesValue() {
var _a, _b;
if (Array.isArray(this._draftConfig.entities)) {
return this._draftConfig.entities.map((entry) => {
var _a2, _b2, _c;
return typeof entry === "string" ? { entity: entry } : {
entity: (_a2 = entry == null ? void 0 : entry.entity) != null ? _a2 : "",
name: (_b2 = entry == null ? void 0 : entry.name) != null ? _b2 : "",
icon: (_c = entry == null ? void 0 : entry.icon) != null ? _c : ""
};
});
}
if (this._draftConfig.entity) {
return [{
entity: this._draftConfig.entity,
name: (_a = this._draftConfig.name) != null ? _a : "",
icon: (_b = this._draftConfig.icon) != null ? _b : ""
}];
}
return [];
}
_getRawEntityRows() {
if (Array.isArray(this._draftConfig.entities)) {
return this._cloneDeep(this._draftConfig.entities);
}
if (this._draftConfig.entity !== void 0) {
const hasTopLevelIdentity = this._draftConfig.name !== void 0 || this._draftConfig.icon !== void 0;
if (!hasTopLevelIdentity) {
return [this._draftConfig.entity];
}
return [{
entity: this._draftConfig.entity,
...this._draftConfig.name !== void 0 ? { name: this._draftConfig.name } : {},
...this._draftConfig.icon !== void 0 ? { icon: this._draftConfig.icon } : {}
}];
}
return [];
}
_buildEntityConfigEntries(entities) {
const usesShorthand = !Array.isArray(this._draftConfig.entities) && this._draftConfig.entity !== void 0;
const source = Array.isArray(this._draftConfig.entities) ? this._draftConfig.entities : this._draftConfig.entity !== void 0 ? [{
entity: this._draftConfig.entity,
...this._draftConfig.name !== void 0 ? { name: this._draftConfig.name } : {},
...this._draftConfig.icon !== void 0 ? { icon: this._draftConfig.icon } : {}
}] : [];
const entries = entities.map((entry, index) => {
const rawEntry = source[index];
if (this._isObject(rawEntry)) {
const mergedEntry = {
...rawEntry,
entity: entry.entity
};
const normalizedName2 = this._normalizeTextValue(entry.name).trim();
if (normalizedName2) {
mergedEntry.name = normalizedName2;
} else {
delete mergedEntry.name;
}
const rawIconValue = entry == null ? void 0 : entry.icon;
const normalizedIcon2 = typeof rawIconValue === "string" ? rawIconValue.trim() : rawIconValue;
if (normalizedIcon2 === false) {
mergedEntry.icon = false;
} else if (typeof normalizedIcon2 === "string" && normalizedIcon2) {
mergedEntry.icon = normalizedIcon2;
} else if (rawEntry.icon === false) {
mergedEntry.icon = false;
} else {
delete mergedEntry.icon;
}
return mergedEntry;
}
const nextEntry = {
entity: entry.entity
};
const normalizedName = this._normalizeTextValue(entry.name).trim();
if (normalizedName) {
nextEntry.name = normalizedName;
}
const normalizedIcon = typeof (entry == null ? void 0 : entry.icon) === "string" ? entry.icon.trim() : entry == null ? void 0 : entry.icon;
if (normalizedIcon === false) {
nextEntry.icon = false;
} else if (typeof normalizedIcon === "string" && normalizedIcon) {
nextEntry.icon = normalizedIcon;
}
return nextEntry;
});
if (!usesShorthand) {
return entries.map((entry) => {
if (this._isObject(entry) && Object.keys(entry).length === 1 && entry.entity !== void 0) {
return entry.entity;
}
return entry;
});
}
return entries;
}
_updateConfig(nextConfig) {
this._draftConfig = this._cloneDeep(nextConfig);
}
_emitConfigChanged() {
const emittedConfig = this._cleanupEditorEmittedConfig(this._cloneDeep(this._draftConfig));
const nextConfigJson = this._serializeConfig(emittedConfig);
if (nextConfigJson === this._lastEmittedConfigJson) {
return false;
}
this._lastEmittedConfigJson = nextConfigJson;
this.dispatchEvent(new CustomEvent("config-changed", {
detail: { config: emittedConfig },
bubbles: true,
composed: true
}));
return true;
}
_scheduleRender() {
if (this._renderScheduled || this._isRendering) return;
this._renderScheduled = true;
setTimeout(() => {
this._renderScheduled = false;
this._render();
}, 0);
}
_applyUserConfig(nextConfig, options = {}) {
const { rerender = false } = options;
const nextConfigJson = this._serializeConfig(nextConfig);
const currentDraftJson = this._serializeConfig(this._draftConfig);
if (nextConfigJson === currentDraftJson) {
return false;
}
this._updateConfig(nextConfig);
this._emitConfigChanged();
this._refreshDerivedEditorUi();
if (rerender) {
this._scheduleRender();
}
return true;
}
_getShadowElementById(id) {
var _a, _b, _c, _d, _e, _f;
if (!this.shadowRoot) {
return null;
}
return (_f = (_e = (_b = (_a = this.shadowRoot).getElementById) == null ? void 0 : _b.call(_a, id)) != null ? _e : (_d = (_c = this.shadowRoot).querySelector) == null ? void 0 : _d.call(_c, `#${id}`)) != null ? _f : null;
}
_setElementChecked(id, checked) {
const element = this._getShadowElementById(id);
if (element) {
element.checked = !!checked;
}
}
_setElementText(id, value) {
const element = this._getShadowElementById(id);
if (element) {
element.textContent = value;
}
}
_refreshDerivedEditorUi() {
if (!this.shadowRoot) {
return;
}
this._refreshCardDerivedUi();
this._refreshEntityDerivedUi();
}
_refreshCardDerivedUi() {
this._setElementText("card-group-marker-target-summary", this._getCardTargetMarkerSummary());
this._setElementText("card-group-marker-peak-summary", this._getMarkerResetSummary("peak"));
this._setElementText("card-group-marker-floor-summary", this._getMarkerResetSummary("floor"));
this._setElementText("card-group-baseline-summary", this._getCardBaselineSummary());
this._setElementText("card-group-generic-markers-summary", this._getGenericMarkersSummary({ type: "card" }));
this._setElementText("card-group-segments-summary", this._getSegmentsSummary({ type: "card" }));
this._setElementText("card-group-gradient-stops-summary", this._getGradientStopsSummary({ type: "card" }));
this._setElementChecked("target-above-fill-enabled", this._isTargetAboveFillEnabled({ type: "card" }));
this._setElementChecked("baseline-above-color-enabled", this._isBaselineDirectionalColorEnabled({ type: "card" }, "above"));
this._setElementChecked("baseline-below-color-enabled", this._isBaselineDirectionalColorEnabled({ type: "card" }, "below"));
this._refreshSegmentUi({ type: "card" });
}
_refreshEntityDerivedUi() {
const count = this._getEntitiesValue().length;
for (let index = 0; index < count; index += 1) {
const scope = { type: "entity", index };
this._setElementChecked(`entity-${index}-scale-inherit`, !this._hasResolvableOverride(this._getResolvableScopedValue(scope, "min")) && !this._hasResolvableOverride(this._getResolvableScopedValue(scope, "max")));
this._setElementChecked(`entity-${index}-target-inherit`, !this._hasTargetOverride(scope));
this._setElementChecked(`entity-${index}-baseline-inherit`, !this._hasBaselineOverride(scope));
this._setElementChecked(`entity-${index}-needle-inherit`, !this._hasNeedleOverride(scope));
this._setElementChecked(`entity-${index}-peak-inherit`, !this._hasPeakOverride(scope));
this._setElementChecked(`entity-${index}-floor-inherit`, !this._hasExtremumOverride(scope, "floor"));
this._setElementChecked(`entity-${index}-markers-inherit`, !this._hasMarkersOverride(scope));
this._setElementChecked(`entity-${index}-bar-inherit`, !this._hasEntityBarAppearanceOverride(scope));
this._setElementChecked(`entity-${index}-segments-inherit`, !this._hasSegmentsOverride(scope));
this._setElementChecked(`entity-${index}-gradient-stops-inherit`, !this._hasGradientStopsOverride(scope));
this._setElementChecked(`entity-${index}-layout-inherit`, !this._hasLayoutOverride(scope));
this._setElementChecked(`entity-${index}-formatting-inherit`, !this._hasFormattingOverride(scope));
this._setElementChecked(`entity-${index}-target-above-fill-enabled`, this._isTargetAboveFillEnabled(scope));
this._setElementChecked(`entity-${index}-baseline-above-color-enabled`, this._isBaselineDirectionalColorEnabled(scope, "above"));
this._setElementChecked(`entity-${index}-baseline-below-color-enabled`, this._isBaselineDirectionalColorEnabled(scope, "below"));
this._setElementText(`entity-${index}-group-scale-summary`, this._getScaleOverrideSummary(scope));
this._setElementText(`entity-${index}-group-target-summary`, this._getTargetOverrideSummary(scope));
this._setElementText(`entity-${index}-group-baseline-summary`, this._getBaselineOverrideSummary(scope));
this._setElementText(`entity-${index}-group-needle-summary`, this._getNeedleSummary(scope));
this._setElementText(`entity-${index}-group-peak-summary`, this._getPeakSummary(scope));
this._setElementText(`entity-${index}-group-floor-summary`, this._getFloorSummary(scope));
this._setElementText(`entity-${index}-group-markers-summary`, this._getGenericMarkersSummary(scope));
this._setElementText(`entity-${index}-group-bar-summary`, this._getBarAppearanceSummary(scope));
this._setElementText(`entity-${index}-group-segments-summary`, this._getSegmentsSummary(scope));
this._setElementText(`entity-${index}-group-gradient-stops-summary`, this._getGradientStopsSummary(scope));
this._setElementText(`entity-${index}-group-layout-summary`, this._getLayoutSummary(scope));
this._setElementText(`entity-${index}-group-formatting-summary`, this._getFormattingSummary(scope));
this._refreshSegmentUi(scope);
}
}
_queuePostRenderFocus(selector) {
this._pendingFocusSelector = selector || null;
}
_applyPendingFocus() {
if (!this._pendingFocusSelector || !this.shadowRoot) {
return;
}
const selector = this._pendingFocusSelector;
this._pendingFocusSelector = null;
setTimeout(() => {
var _a, _b;
const element = (_b = (_a = this.shadowRoot) == null ? void 0 : _a.querySelector) == null ? void 0 : _b.call(_a, selector);
if (!element || typeof element.focus !== "function") {
return;
}
try {
element.focus({ preventScroll: true });
} catch (_error) {
element.focus();
}
}, 0);
}
_setValueAtPath(path, value, options = {}) {
const nextConfig = value === void 0 ? this._deletePathValue(this._draftConfig, path) : this._setPathValue(this._draftConfig, path, value);
return this._applyUserConfig(nextConfig, options);
}
_setTitle(value) {
this._setValueAtPath(["title"], value);
}
_setEntityField(index, key, value) {
const normalizedValue = this._normalizeTextValue(value);
if (!Array.isArray(this._draftConfig.entities) && this._draftConfig.entity !== void 0 && index === 0) {
if (!normalizedValue.trim()) {
return this._setValueAtPath([key], void 0);
}
return this._setValueAtPath([key], normalizedValue.trim());
}
const nextConfig = this._withEntityScopeConfig((entries) => {
var _a;
const rawEntry = entries[index];
const nextEntry = this._isObject(rawEntry) ? this._cloneDeep(rawEntry) : { entity: (_a = rawEntry == null ? void 0 : rawEntry.entity) != null ? _a : "" };
if (!normalizedValue.trim()) {
delete nextEntry[key];
} else {
nextEntry[key] = normalizedValue.trim();
}
entries[index] = nextEntry;
return entries;
});
return this._applyUserConfig(nextConfig);
}
_cleanupEntityIdentityForEmit(target) {
if (!this._isObject(target)) {
return target;
}
const nextTarget = this._cloneDeep(target);
const normalizedName = this._normalizeTextValue(nextTarget.name).trim();
if (!normalizedName) {
delete nextTarget.name;
} else {
nextTarget.name = normalizedName;
}
if (nextTarget.icon === false) {
return nextTarget;
}
const normalizedIcon = this._normalizeTextValue(nextTarget.icon).trim();
if (!normalizedIcon) {
delete nextTarget.icon;
} else {
nextTarget.icon = normalizedIcon;
}
return nextTarget;
}
_cleanupNeedleForEmit(target, scope = { type: "card" }) {
var _a;
if (!this._isObject(target) || !this._isObject(target.bar)) {
return target;
}
const nextTarget = this._cloneDeep(target);
const rawNeedle = (_a = nextTarget.bar) == null ? void 0 : _a.needle;
if (rawNeedle === void 0) {
return nextTarget;
}
const defaultColor = this._normalizeColorComparisonValue("#ffffff");
let nextNeedle = null;
if (rawNeedle === true) {
nextNeedle = { show: true };
} else if (rawNeedle === false) {
nextNeedle = (scope == null ? void 0 : scope.type) === "entity" ? { show: false } : null;
} else if (this._isObject(rawNeedle)) {
const color = this._normalizeTextValue(rawNeedle.color).trim();
if (rawNeedle.show === true) {
nextNeedle = { show: true };
} else if (rawNeedle.show === false) {
nextNeedle = (scope == null ? void 0 : scope.type) === "entity" ? { show: false } : null;
} else if ((scope == null ? void 0 : scope.type) === "entity" && color && this._normalizeColorComparisonValue(color) !== defaultColor) {
nextNeedle = {};
}
if (nextNeedle && color && this._normalizeColorComparisonValue(color) !== defaultColor) {
nextNeedle.color = color;
}
} else {
nextNeedle = null;
}
if (nextNeedle) {
nextTarget.bar.needle = nextNeedle;
} else {
delete nextTarget.bar.needle;
if (!Object.keys(nextTarget.bar).length) {
delete nextTarget.bar;
}
}
return nextTarget;
}
_cleanupResolvableValueForEmit(value) {
if (this._isObject(value) || value === null || typeof value === "string" && this._normalizeNumberValue(value) === null) {
return this._cloneDeep(value);
}
const fixed = this._normalizeNumberValue(value);
return fixed === null ? null : { fixed };
}
_cleanupScaleForEmit(target) {
if (!this._isObject(target) || !this._isObject(target.scale)) {
return target;
}
const nextTarget = this._cloneDeep(target);
const nextScale = this._cloneDeep(nextTarget.scale);
["min", "max"].forEach((key) => {
if (!Object.prototype.hasOwnProperty.call(nextScale, key)) {
return;
}
const cleanedValue = this._cleanupResolvableValueForEmit(nextScale[key]);
if (cleanedValue || nextScale[key] === null) {
nextScale[key] = cleanedValue;
delete nextTarget[key];
delete nextTarget[`${key}_entity`];
} else {
delete nextScale[key];
}
});
if (Object.keys(nextScale).length) {
nextTarget.scale = nextScale;
} else {
delete nextTarget.scale;
}
return nextTarget;
}
_cleanupFormattingForEmit(target) {
if (!this._isObject(target) || !this._isObject(target.formatting)) {
return target;
}
const nextTarget = this._cloneDeep(target);
const nextFormatting = this._cloneDeep(nextTarget.formatting);
const unit = this._normalizeTextValue(nextFormatting.unit).trim();
const decimal = this._normalizeDecimalValue(nextFormatting.decimal);
if (unit) {
nextFormatting.unit = unit;
delete nextTarget.unit;
} else {
delete nextFormatting.unit;
}
if (decimal !== null) {
nextFormatting.decimal = decimal;
delete nextTarget.decimal;
} else {
delete nextFormatting.decimal;
}
if (Object.keys(nextFormatting).length) {
nextTarget.formatting = nextFormatting;
} else {
delete nextTarget.formatting;
}
return nextTarget;
}
_cleanupLayoutForEmit(target) {
if (!this._isObject(target) || !this._isObject(target.layout)) {
return target;
}
const nextTarget = this._cloneDeep(target);
const nextLayout = this._cloneDeep(nextTarget.layout);
const nextLabel = this._isObject(nextLayout.label) ? this._cloneDeep(nextLayout.label) : null;
const nextHero = this._isObject(nextLayout.hero) ? this._cloneDeep(nextLayout.hero) : null;
const height = this._normalizeNumberValue(nextLayout.height);
const width = this._normalizeNumberValue(nextLabel == null ? void 0 : nextLabel.width);
const valueSize = this._normalizeHeroValueSizeValue(nextHero == null ? void 0 : nextHero.value_size);
const position = this._normalizeTextValue(nextLabel == null ? void 0 : nextLabel.position).trim();
if (height !== null && height >= 24) {
nextLayout.height = height;
delete nextTarget.height;
} else {
delete nextLayout.height;
}
if (nextLabel) {
if (position) {
nextLabel.position = position;
delete nextTarget.label_position;
} else {
delete nextLabel.position;
}
if (width !== null) {
nextLabel.width = width;
delete nextTarget.label_width;
} else {
delete nextLabel.width;
}
if (Object.keys(nextLabel).length) {
nextLayout.label = nextLabel;
} else {
delete nextLayout.label;
}
}
if (nextHero) {
if (valueSize !== null) {
nextHero.value_size = valueSize;
} else {
delete nextHero.value_size;
}
if (Object.keys(nextHero).length) {
nextLayout.hero = nextHero;
} else {
delete nextLayout.hero;
}
}
if (Object.keys(nextLayout).length) {
nextTarget.layout = nextLayout;
} else {
delete nextTarget.layout;
}
return nextTarget;
}
_cleanupTargetForEmit(target, scope = { type: "card" }) {
var _a;
if (!this._isObject(target) || !this._isObject(target.target)) {
return target;
}
const nextTarget = this._cloneDeep(target);
const nextMarker = this._cloneDeep(nextTarget.target);
const cleanedAt = this._cleanupResolvableValueForEmit(nextMarker.at);
const color = this._normalizeTextValue(nextMarker.color).trim();
const label = this._cleanBuiltinMarkerLabelForEmit(nextMarker.label, scope, "target");
const fillColor = this._normalizeTextValue((_a = nextMarker.when_exceeded) == null ? void 0 : _a.fill_color).trim();
const hasShape = Object.prototype.hasOwnProperty.call(nextMarker, "shape");
const shape = hasShape ? normalizeTargetMarkerShape(nextMarker.shape) : null;
const direction = Object.prototype.hasOwnProperty.call(nextMarker, "direction") ? normalizeMarkerDirection(nextMarker.direction) : null;
if (typeof nextMarker.enabled !== "boolean") {
delete nextMarker.enabled;
}
if (cleanedAt || nextMarker.at === null) {
nextMarker.at = cleanedAt;
delete nextTarget.target_entity;
} else {
delete nextMarker.at;
}
if (color && this._normalizeColorComparisonValue(color) !== this._normalizeColorComparisonValue("#888")) {
nextMarker.color = color;
delete nextTarget.target_color;
} else {
delete nextMarker.color;
}
if (Object.keys(label).length) {
nextMarker.label = label;
if (label.show === true) delete nextTarget.show_target_label;
} else {
delete nextMarker.label;
delete nextTarget.show_target_label;
}
if (fillColor) {
nextMarker.when_exceeded = {
...this._isObject(nextMarker.when_exceeded) ? nextMarker.when_exceeded : {},
fill_color: fillColor
};
delete nextTarget.above_target_color;
} else {
delete nextMarker.when_exceeded;
}
if ((scope == null ? void 0 : scope.type) === "card" && shape === "diamond") {
delete nextMarker.shape;
} else if (shape) {
nextMarker.shape = shape;
} else {
delete nextMarker.shape;
}
if (direction === "inward" && ((scope == null ? void 0 : scope.type) !== "entity" || direction === this._getEffectiveMarkerDirection({ type: "card" }, "target"))) {
delete nextMarker.direction;
} else if (direction) {
nextMarker.direction = direction;
}
if (Object.keys(nextMarker).length) {
nextTarget.target = nextMarker;
} else {
delete nextTarget.target;
}
return nextTarget;
}
_cleanupBaselineForEmit(target) {
if (!this._isObject(target) || !this._isObject(target.baseline)) {
return target;
}
const nextTarget = this._cloneDeep(target);
const nextBaseline = this._cloneDeep(nextTarget.baseline);
const cleanedAt = this._cleanupResolvableValueForEmit(nextBaseline.at);
if (typeof nextBaseline.enabled !== "boolean") {
delete nextBaseline.enabled;
}
if (cleanedAt || nextBaseline.at === null) {
nextBaseline.at = cleanedAt;
} else {
delete nextBaseline.at;
}
["above", "below"].forEach((direction) => {
if (!this._isObject(nextBaseline[direction])) {
delete nextBaseline[direction];
return;
}
const color = this._normalizeTextValue(nextBaseline[direction].color).trim();
if (color) {
nextBaseline[direction] = { ...nextBaseline[direction], color };
} else {
delete nextBaseline[direction];
}
});
if (Object.keys(nextBaseline).length) {
nextTarget.baseline = nextBaseline;
} else {
delete nextTarget.baseline;
}
return nextTarget;
}
_cleanupPeakForEmit(target, scope = { type: "card" }) {
if (!this._isObject(target) || !this._isObject(target.peak)) {
return target;
}
const nextTarget = this._cloneDeep(target);
const nextPeak = this._cloneDeep(nextTarget.peak);
const color = this._normalizeTextValue(nextPeak.color).trim();
const direction = Object.prototype.hasOwnProperty.call(nextPeak, "direction") ? normalizeMarkerDirection(nextPeak.direction) : null;
if (typeof nextPeak.enabled !== "boolean") {
delete nextPeak.enabled;
}
if ((scope == null ? void 0 : scope.type) !== "entity" && nextPeak.reset === "never") {
delete nextPeak.reset;
}
if (direction === "inward" && ((scope == null ? void 0 : scope.type) !== "entity" || direction === this._getEffectiveMarkerDirection({ type: "card" }, "peak"))) {
delete nextPeak.direction;
} else if (direction) {
nextPeak.direction = direction;
}
if (color && this._normalizeColorComparisonValue(color) !== this._normalizeColorComparisonValue("#888")) {
nextPeak.color = color;
delete nextTarget.peak_color;
} else {
delete nextPeak.color;
}
nextPeak.label = this._cleanBuiltinMarkerLabelForEmit(nextPeak.label, scope, "peak");
if (!Object.keys(nextPeak.label).length) delete nextPeak.label;
if (Object.keys(nextPeak).length) {
nextTarget.peak = nextPeak;
} else {
delete nextTarget.peak;
}
return nextTarget;
}
_cleanupFloorForEmit(target, scope = { type: "card" }) {
if (!this._isObject(target) || !this._isObject(target.floor)) {
return target;
}
const nextTarget = this._cloneDeep(target);
const nextFloor = this._cloneDeep(nextTarget.floor);
const color = this._normalizeTextValue(nextFloor.color).trim();
const direction = Object.prototype.hasOwnProperty.call(nextFloor, "direction") ? normalizeMarkerDirection(nextFloor.direction) : null;
if (typeof nextFloor.enabled !== "boolean") delete nextFloor.enabled;
if (color && this._normalizeColorComparisonValue(color) !== this._normalizeColorComparisonValue("#888888")) {
nextFloor.color = color;
} else {
delete nextFloor.color;
}
if (this._isObject(nextFloor.label)) {
nextFloor.label = this._cleanBuiltinMarkerLabelForEmit(nextFloor.label, scope, "floor");
if (!Object.keys(nextFloor.label).length) delete nextFloor.label;
}
if ((scope == null ? void 0 : scope.type) !== "entity" && nextFloor.reset === "never" || nextFloor.reset === void 0 || nextFloor.reset === null || nextFloor.reset === "") {
delete nextFloor.reset;
}
if (direction === "inward" && ((scope == null ? void 0 : scope.type) !== "entity" || direction === this._getEffectiveMarkerDirection({ type: "card" }, "floor"))) {
delete nextFloor.direction;
} else if (direction) {
nextFloor.direction = direction;
}
if (Object.keys(nextFloor).length) nextTarget.floor = nextFloor;
else delete nextTarget.floor;
return nextTarget;
}
_cleanBuiltinMarkerLabelForEmit(rawLabel, scope, key) {
var _a, _b, _c;
const label = this._isObject(rawLabel) ? this._cloneDeep(rawLabel) : {};
const cardLabel = (scope == null ? void 0 : scope.type) === "entity" ? (_a = this._getScopedValue({ type: "card" }, [key, "label"])) != null ? _a : {} : {};
if (label.show === false && (scope == null ? void 0 : scope.type) !== "entity") delete label.show;
if (label.show === false && (scope == null ? void 0 : scope.type) === "entity" && cardLabel.show !== true) delete label.show;
if (label.show_value === true && ((scope == null ? void 0 : scope.type) !== "entity" || cardLabel.show_value !== false)) delete label.show_value;
if (label.show_unit === true && ((scope == null ? void 0 : scope.type) !== "entity" || cardLabel.show_unit !== false)) delete label.show_unit;
if (typeof label.text === "string") {
label.text = label.text.replace(/\s+/g, " ").trim();
if (!label.text && (scope == null ? void 0 : scope.type) !== "entity") delete label.text;
}
const precision = this._normalizeDecimalValue((_b = label.precision) != null ? _b : label.decimal);
delete label.decimal;
if (precision === null) delete label.precision;
else if ((scope == null ? void 0 : scope.type) === "entity" && precision === ((_c = cardLabel.precision) != null ? _c : cardLabel.decimal)) delete label.precision;
else label.precision = precision;
return label;
}
_cleanupBarForEmit(target, scope = { type: "card" }, cardConfig = null, inheritedSegmentSpace = null) {
if (!this._isObject(target) || !this._isObject(target.bar)) {
return target;
}
const nextTarget = this._cloneDeep(target);
const nextBar = this._cloneDeep(nextTarget.bar);
const scopedSegmentSpace = ["percent", "scale"].includes(nextBar.segment_space) ? nextBar.segment_space : (scope == null ? void 0 : scope.type) === "entity" && ["percent", "scale"].includes(inheritedSegmentSpace) ? inheritedSegmentSpace : null;
const legacySegmentSpace = scopedSegmentSpace;
if (legacySegmentSpace === "percent" && Array.isArray(nextBar.segments)) {
const asLegacyPercent = (boundary) => {
if (typeof boundary === "number" && Number.isFinite(boundary)) return `${boundary}%`;
if (typeof boundary === "string" && boundary.trim() !== "" && !boundary.includes("%") && Number.isFinite(Number(boundary.trim()))) {
return `${Number(boundary.trim())}%`;
}
return boundary;
};
nextBar.segments = nextBar.segments.map((segment) => ({
...segment,
from: asLegacyPercent(segment == null ? void 0 : segment.from),
...Object.prototype.hasOwnProperty.call(segment != null ? segment : {}, "to") ? { to: asLegacyPercent(segment.to) } : {}
}));
}
delete nextBar.segment_space;
const fillStyle = this._normalizeTextValue(nextBar.fill_style).trim();
const color = this._normalizeTextValue(nextBar.color).trim();
const segments = Array.isArray(nextBar.segments) ? nextBar.segments : null;
const gradientStops = Array.isArray(nextBar.gradient_stops) ? nextBar.gradient_stops : null;
if (fillStyle) {
const withoutLocalFillStyle = this._cloneDeep(nextTarget);
delete withoutLocalFillStyle.bar.fill_style;
const inheritedStyle = normalizeBarConfig(
withoutLocalFillStyle,
(scope == null ? void 0 : scope.type) === "entity" ? cardConfig : null,
{ isCardScope: (scope == null ? void 0 : scope.type) !== "entity" }
).fill_style;
if (fillStyle === "bands" && inheritedStyle === "bands") delete nextBar.fill_style;
else nextBar.fill_style = fillStyle;
delete nextTarget.color_mode;
} else {
delete nextBar.fill_style;
}
if (color && this._normalizeColorComparisonValue(color) !== this._normalizeColorComparisonValue("#4a9eff")) {
nextBar.color = color;
delete nextTarget.color;
} else {
delete nextBar.color;
}
if (nextBar.solid_fill === true) {
nextBar.solid_fill = true;
} else {
delete nextBar.solid_fill;
}
if (segments && (!segments.length || legacySegmentSpace || !this._segmentsEqualForEditor(segments, this._getDefaultSegments()))) {
nextBar.segments = segments;
delete nextTarget.segments;
delete nextTarget.severity;
} else {
delete nextBar.segments;
}
if (gradientStops && (!gradientStops.length || gradientStops.length >= 2 && !this._isDefaultGradientStops(gradientStops))) {
nextBar.gradient_stops = this._sanitizeGradientStopsForEmit(gradientStops);
delete nextTarget.gradient_stops;
} else {
delete nextBar.gradient_stops;
}
if (Object.keys(nextBar).length) {
nextTarget.bar = nextBar;
} else {
delete nextTarget.bar;
}
return nextTarget;
}
_getEditorKnownKeyOrder(path = []) {
const pathKey = path.join(".");
switch (pathKey) {
case "":
return ["type", "title", "entities", "scale", "target", "baseline", "peak", "floor", "layout", "formatting", "bar"];
case "entities.*":
return ["entity", "name", "icon", "scale", "target", "baseline", "peak", "floor", "layout", "formatting", "bar"];
case "scale":
return ["min", "max"];
case "scale.min":
case "scale.max":
case "target.at":
case "baseline.at":
return ["fixed", "entity"];
case "target":
return ["enabled", "at", "shape", "color", "label", "when_exceeded"];
case "target.label":
return ["show", "decimal"];
case "target.when_exceeded":
return ["fill_color"];
case "baseline":
return ["enabled", "at", "above", "below"];
case "baseline.above":
case "baseline.below":
return ["color"];
case "peak":
return ["enabled", "color", "reset", "label"];
case "floor":
return ["enabled", "color", "reset", "label"];
case "peak.label":
case "floor.label":
return ["show", "decimal"];
case "layout":
return ["height", "label", "hero"];
case "layout.label":
return ["position", "hero_size", "width"];
case "layout.hero":
return ["size", "value_size"];
case "formatting":
return ["unit", "decimal"];
case "bar":
return ["fill_style", "color", "solid_fill", "needle", "segments", "gradient_stops"];
case "bar.needle":
return ["show", "color"];
default:
return null;
}
}
_orderEditorConfigKeys(value, path = []) {
var _a;
if (Array.isArray(value)) {
const nextPath = path[0] === "entities" ? ["entities", "*"] : path;
return value.map((entry) => this._orderEditorConfigKeys(entry, nextPath));
}
if (!this._isObject(value)) {
return value;
}
const orderedValue = {};
const knownOrder = (_a = this._getEditorKnownKeyOrder(path)) != null ? _a : [];
const seenKeys = /* @__PURE__ */ new Set();
knownOrder.forEach((key) => {
if (!Object.prototype.hasOwnProperty.call(value, key)) {
return;
}
orderedValue[key] = this._orderEditorConfigKeys(value[key], [...path, key]);
seenKeys.add(key);
});
Object.keys(value).forEach((key) => {
if (seenKeys.has(key)) {
return;
}
orderedValue[key] = this._orderEditorConfigKeys(value[key], [...path, key]);
});
return orderedValue;
}
_cleanupEditorEmittedConfig(config) {
var _a;
if (!this._isObject(config)) {
return config;
}
const inheritedSegmentSpace = (_a = config.bar) == null ? void 0 : _a.segment_space;
let nextConfig = this._cleanupEntityIdentityForEmit(config);
nextConfig = this._cleanupScaleForEmit(nextConfig);
nextConfig = this._cleanupTargetForEmit(nextConfig, { type: "card" });
nextConfig = this._cleanupBaselineForEmit(nextConfig);
nextConfig = this._cleanupPeakForEmit(nextConfig, { type: "card" });
nextConfig = this._cleanupFloorForEmit(nextConfig, { type: "card" });
nextConfig = this._cleanupGenericMarkersForEmit(nextConfig);
nextConfig = this._cleanupLayoutForEmit(nextConfig);
nextConfig = this._cleanupFormattingForEmit(nextConfig);
nextConfig = this._cleanupNeedleForEmit(nextConfig, { type: "card" });
nextConfig = this._cleanupBarForEmit(nextConfig, { type: "card" });
if (Array.isArray(nextConfig.entities)) {
nextConfig.entities = nextConfig.entities.map((entry, index) => {
if (!this._isObject(entry)) {
return entry;
}
let cleanedEntry = this._cleanupEntityIdentityForEmit(entry);
cleanedEntry = this._cleanupScaleForEmit(cleanedEntry);
cleanedEntry = this._cleanupTargetForEmit(cleanedEntry, { type: "entity", index });
cleanedEntry = this._cleanupBaselineForEmit(cleanedEntry);
cleanedEntry = this._cleanupPeakForEmit(cleanedEntry, { type: "entity", index });
cleanedEntry = this._cleanupFloorForEmit(cleanedEntry, { type: "entity", index });
cleanedEntry = this._cleanupGenericMarkersForEmit(cleanedEntry);
cleanedEntry = this._cleanupLayoutForEmit(cleanedEntry);
cleanedEntry = this._cleanupFormattingForEmit(cleanedEntry);
cleanedEntry = this._cleanupNeedleForEmit(cleanedEntry, { type: "entity" });
cleanedEntry = this._cleanupBarForEmit(cleanedEntry, { type: "entity", index }, nextConfig, inheritedSegmentSpace);
return cleanedEntry;
});
}
const meaning = (raw) => {
const normalized = normalizeCardConfig({ ...raw, ...!raw.entities && !raw.entity ? { entities: [] } : {} });
const featureKeys = ["layout", "scale", "bar", "baseline", "formatting", "target_marker", "peak_marker", "floor_marker", "generic_markers"];
const scope = (value) => ({
...Object.fromEntries(featureKeys.map((key) => [key, value[key]])),
scale: Object.fromEntries(["min", "max"].map((key) => [key, {
...value.scale[key],
fixed_explicit: value.scale[key].fixed_explicit !== false
}]))
});
const canonical = (value, key = "") => {
if (Array.isArray(value)) return value.map((entry) => canonical(entry));
if (this._isObject(value)) return Object.fromEntries(Object.entries(value).filter(([name]) => !["severity", "segment_space", "label_precision_key"].includes(name) && !/invalid/i.test(name)).map(([name, entry]) => [name, canonical(entry, name)]));
if (key === "fixed") return getNumericValue(null, value);
if (/color/i.test(key) && typeof value === "string") return this._normalizeColorComparisonValue(value);
return value;
};
return this._serializeConfig(canonical({ card: scope(normalized), entities: normalized.entities.map((row) => ({
entity: row.entity,
name: row.name,
icon: row.icon,
...scope(row)
})) }));
};
try {
if (meaning(config) !== meaning(nextConfig)) nextConfig = this._cloneDeep(config);
} catch (_error) {
nextConfig = this._cloneDeep(config);
}
return this._orderEditorConfigKeys(nextConfig);
}
_getScopedPath(scope, keyPath) {
const normalizedPath = Array.isArray(keyPath) ? keyPath : [keyPath];
if (!scope || scope.type === "card") {
return normalizedPath;
}
if (scope.type === "entity") {
return ["entities", scope.index, ...normalizedPath];
}
return normalizedPath;
}
_normalizePath(keyPath) {
return Array.isArray(keyPath) ? keyPath : [keyPath];
}
_getEntityRawEntries() {
if (Array.isArray(this._draftConfig.entities)) {
return this._draftConfig.entities.map((entry) => this._isObject(entry) ? this._cloneDeep(entry) : { entity: entry });
}
if (this._draftConfig.entity !== void 0) {
return [{
entity: this._draftConfig.entity,
...this._draftConfig.name !== void 0 ? { name: this._draftConfig.name } : {},
...this._draftConfig.icon !== void 0 ? { icon: this._draftConfig.icon } : {}
}];
}
return [];
}
_withEntityScopeConfig(mutator) {
const rawEntries = this._getEntityRawEntries();
const nextEntries = mutator(rawEntries.map((entry) => this._cloneDeep(entry)));
let nextConfig = this._setPathValue(this._draftConfig, ["entities"], nextEntries);
if (!Array.isArray(this._draftConfig.entities) && this._draftConfig.entity !== void 0) {
nextConfig = this._deletePathValue(nextConfig, ["entity"]);
if (this._draftConfig.name !== void 0) {
nextConfig = this._deletePathValue(nextConfig, ["name"]);
}
if (this._draftConfig.icon !== void 0) {
nextConfig = this._deletePathValue(nextConfig, ["icon"]);
}
}
return nextConfig;
}
_setEntityRowsRaw(nextRows, options = {}) {
var _a;
const normalizedRows = this._cloneDeep(nextRows);
if (Array.isArray(this._draftConfig.entities) || this._draftConfig.entity === void 0 || normalizedRows.length !== 1) {
let nextConfig2 = this._setPathValue(this._draftConfig, ["entities"], normalizedRows);
if (!Array.isArray(this._draftConfig.entities) && this._draftConfig.entity !== void 0) {
nextConfig2 = this._deletePathValue(nextConfig2, ["entity"]);
nextConfig2 = this._deletePathValue(nextConfig2, ["name"]);
nextConfig2 = this._deletePathValue(nextConfig2, ["icon"]);
}
return this._applyUserConfig(nextConfig2, options);
}
const [row] = normalizedRows;
if (typeof row === "string") {
let nextConfig2 = this._setPathValue(this._draftConfig, ["entity"], row);
nextConfig2 = this._deletePathValue(nextConfig2, ["name"]);
nextConfig2 = this._deletePathValue(nextConfig2, ["icon"]);
return this._applyUserConfig(nextConfig2, options);
}
let nextConfig = this._setPathValue(this._draftConfig, ["entity"], (_a = row == null ? void 0 : row.entity) != null ? _a : "");
if (row && Object.prototype.hasOwnProperty.call(row, "name")) {
nextConfig = this._setPathValue(nextConfig, ["name"], row.name);
} else {
nextConfig = this._deletePathValue(nextConfig, ["name"]);
}
if (row && Object.prototype.hasOwnProperty.call(row, "icon")) {
nextConfig = this._setPathValue(nextConfig, ["icon"], row.icon);
} else {
nextConfig = this._deletePathValue(nextConfig, ["icon"]);
}
return this._applyUserConfig(nextConfig, options);
}
_moveEntityRow(index, direction) {
const rows = this._getRawEntityRows();
const nextIndex = index + direction;
if (index < 0 || index >= rows.length || nextIndex < 0 || nextIndex >= rows.length) {
return false;
}
const nextRows = this._cloneDeep(rows);
[nextRows[index], nextRows[nextIndex]] = [nextRows[nextIndex], nextRows[index]];
return this._setEntityRowsRaw(nextRows, { rerender: true });
}
_duplicateEntityRow(index) {
const rows = this._getRawEntityRows();
if (index < 0 || index >= rows.length) {
return false;
}
const sourceRow = this._cloneDeep(rows[index]);
const duplicateRow = typeof sourceRow === "string" ? { entity: sourceRow } : this._cloneDeep(sourceRow);
if (this._isObject(duplicateRow) && typeof duplicateRow.name === "string" && duplicateRow.name.trim()) {
duplicateRow.name = `${duplicateRow.name.trim()} copy`;
}
const nextRows = this._cloneDeep(rows);
nextRows.splice(index + 1, 0, duplicateRow);
return this._setEntityRowsRaw(nextRows, { rerender: true });
}
_removeEntityRow(index) {
const rows = this._getRawEntityRows();
if (rows.length <= 1 || index < 0 || index >= rows.length) {
return false;
}
const nextRows = rows.filter((_, rowIndex) => rowIndex !== index);
return this._setEntityRowsRaw(nextRows, { rerender: true });
}
_getScopedValue(scope, keyPath) {
if ((scope == null ? void 0 : scope.type) === "entity") {
const entry = this._getEntityRawEntries()[scope.index];
return this._getPathValue(entry, this._normalizePath(keyPath));
}
return this._getPathValue(this._draftConfig, this._getScopedPath(scope, keyPath));
}
_removeScopedValue(scope, keyPath, options = {}) {
if ((scope == null ? void 0 : scope.type) === "entity") {
const nextConfig = this._withEntityScopeConfig((entries) => {
var _a, _b;
const entry = this._isObject(entries[scope.index]) ? { ...entries[scope.index] } : { entity: (_b = (_a = entries[scope.index]) == null ? void 0 : _a.entity) != null ? _b : "" };
entries[scope.index] = this._deletePathValue(entry, this._normalizePath(keyPath));
return entries;
});
return this._applyUserConfig(nextConfig, options);
}
return this._setValueAtPath(this._getScopedPath(scope, keyPath), void 0, options);
}
_applyScopedMutation(scope, mutator, options = {}) {
if ((scope == null ? void 0 : scope.type) === "entity") {
const nextConfig2 = this._withEntityScopeConfig((entries) => {
var _a;
const rawEntry = entries[scope.index];
const entry = this._isObject(rawEntry) ? this._cloneDeep(rawEntry) : { entity: (_a = rawEntry == null ? void 0 : rawEntry.entity) != null ? _a : "" };
entries[scope.index] = mutator(entry);
return entries;
});
return this._applyUserConfig(nextConfig2, options);
}
const nextConfig = mutator(this._cloneDeep(this._draftConfig));
return this._applyUserConfig(nextConfig, options);
}
_setScopedValue(scope, keyPath, value, options = {}) {
return this._applyScopedMutation(scope, (target) => this._setPathValue(target, this._normalizePath(keyPath), value), options);
}
_removePathsFromTarget(target, keyPaths = []) {
return keyPaths.reduce((nextTarget, keyPath) => this._deletePathValue(nextTarget, this._normalizePath(keyPath)), target);
}
_pruneEmptyObjectsInTarget(target, keyPath) {
let nextTarget = target;
const normalizedPath = this._normalizePath(keyPath);
for (let index = normalizedPath.length; index > 0; index--) {
const currentPath = normalizedPath.slice(0, index);
const currentValue = this._getPathValue(nextTarget, currentPath);
if (!this._isObject(currentValue) || Object.keys(currentValue).length) {
break;
}
nextTarget = this._deletePathValue(nextTarget, currentPath);
}
return nextTarget;
}
_setCanonicalScopedValue(scope, canonicalPath, value, options = {}) {
const { deprecatedKeys = [], prunePaths = [] } = options;
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._setPathValue(target, this._normalizePath(canonicalPath), value);
nextTarget = this._removePathsFromTarget(nextTarget, deprecatedKeys);
const pathsToPrune = [this._normalizePath(canonicalPath).slice(0, -1), ...prunePaths.map((path) => this._normalizePath(path))];
pathsToPrune.forEach((path) => {
if (path.length) {
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, path);
}
});
return nextTarget;
}, options);
}
_removeCanonicalScopedValue(scope, canonicalPath, options = {}) {
const { deprecatedKeys = [], prunePaths = [] } = options;
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._deletePathValue(target, this._normalizePath(canonicalPath));
nextTarget = this._removePathsFromTarget(nextTarget, deprecatedKeys);
const pathsToPrune = [this._normalizePath(canonicalPath).slice(0, -1), ...prunePaths.map((path) => this._normalizePath(path))];
pathsToPrune.forEach((path) => {
if (path.length) {
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, path);
}
});
return nextTarget;
}, options);
}
_setScopedNumericOverride(scope, keyPath, rawValue, options = {}) {
const normalizedValue = this._normalizeNumberValue(rawValue);
if (rawValue === "" || rawValue === null || rawValue === void 0) {
return this._removeScopedValue(scope, keyPath, options);
}
if (normalizedValue === null) {
return false;
}
return this._setScopedValue(scope, keyPath, normalizedValue, options);
}
_setScopedTextOverride(scope, keyPath, rawValue, options = {}) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
if (!normalizedValue) {
return this._removeScopedValue(scope, keyPath, options);
}
return this._setScopedValue(scope, keyPath, normalizedValue, options);
}
_setCanonicalScopedNumericOverride(scope, canonicalPath, rawValue, options = {}) {
const normalizedValue = this._normalizeNumberValue(rawValue);
if (rawValue === "" || rawValue === null || rawValue === void 0 || normalizedValue === null) {
return this._removeCanonicalScopedValue(scope, canonicalPath, options);
}
return this._setCanonicalScopedValue(scope, canonicalPath, normalizedValue, options);
}
_setCanonicalScopedTextOverride(scope, canonicalPath, rawValue, options = {}) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
if (!normalizedValue) {
return this._removeCanonicalScopedValue(scope, canonicalPath, options);
}
return this._setCanonicalScopedValue(scope, canonicalPath, normalizedValue, options);
}
_getResolvablePartsFromTarget(target, field, options = {}) {
var _a, _b, _c, _d, _e, _f;
const canonicalBasePath = (_a = options.canonicalBasePath) != null ? _a : ["scale", field];
const legacyFixedPath = (_b = options.legacyFixedPath) != null ? _b : [field];
const legacyEntityPath = (_c = options.legacyEntityPath) != null ? _c : [`${field}_entity`];
const structuredValue = this._getPathValue(target, canonicalBasePath);
const legacyFixedValue = this._getPathValue(target, legacyFixedPath);
const legacyEntityValue = this._getPathValue(target, legacyEntityPath);
if (structuredValue !== void 0) {
const source = normalizeStructuredResolvableValue(structuredValue, (_d = options.inheritedSource) != null ? _d : null, null, {
allowPercent: ["target", "baseline"].includes(canonicalBasePath[0])
});
return {
fixed: (_e = source.fixed) != null ? _e : "",
entity: (_f = source.entity) != null ? _f : "",
...Number.isFinite(source.percent) ? { percent: source.percent } : {}
};
}
return {
fixed: !this._isObject(legacyFixedValue) && legacyFixedValue !== void 0 ? legacyFixedValue : "",
entity: legacyEntityValue != null ? legacyEntityValue : ""
};
}
_getResolvableScopedValue(scope, field, options = {}) {
const target = (scope == null ? void 0 : scope.type) === "entity" ? this._getEntityRawEntries()[scope.index] : this._draftConfig;
return this._getResolvablePartsFromTarget(target != null ? target : {}, field, options);
}
_getEffectiveResolvableScopedValue(scope, field, options = {}) {
var _a, _b;
const localValue = this._getResolvableScopedValue(scope, field, options);
if ((scope == null ? void 0 : scope.type) !== "entity") {
return localValue;
}
const inheritedValue = this._getResolvableScopedValue({ type: "card" }, field, options);
const target = this._getEntityRawEntries()[scope.index];
if (this._getPathValue(target, (_a = options.canonicalBasePath) != null ? _a : ["scale", field]) !== void 0) {
return this._getResolvablePartsFromTarget(target, field, {
...options,
inheritedSource: { fixed: inheritedValue.fixed === "" ? null : inheritedValue.fixed, entity: inheritedValue.entity || null, percent: (_b = inheritedValue.percent) != null ? _b : null }
});
}
return {
fixed: this._hasExplicitOverrideValue(localValue.fixed) ? localValue.fixed : inheritedValue.fixed,
entity: this._hasExplicitOverrideValue(localValue.entity) ? localValue.entity : inheritedValue.entity
};
}
_setCanonicalResolvablePart(scope, field, part, rawValue, options = {}) {
var _a, _b, _c, _d;
const canonicalBasePath = (_a = options.canonicalBasePath) != null ? _a : ["scale", field];
const legacyFixedPath = (_b = options.legacyFixedPath) != null ? _b : [field];
const legacyEntityPath = (_c = options.legacyEntityPath) != null ? _c : [`${field}_entity`];
const prunePaths = (_d = options.prunePaths) != null ? _d : [canonicalBasePath, canonicalBasePath.slice(0, -1)];
const normalizedValue = part === "fixed" ? this._normalizeNumberValue(rawValue) : this._normalizeTextValue(rawValue).trim();
return this._applyScopedMutation(scope, (target) => {
const currentParts = this._getResolvablePartsFromTarget(target != null ? target : {}, field, {
canonicalBasePath,
legacyFixedPath,
legacyEntityPath
});
const nextParts = { ...currentParts };
if (part === "fixed") {
if (rawValue === "" || rawValue === null || rawValue === void 0 || normalizedValue === null) {
delete nextParts.fixed;
} else {
nextParts.fixed = normalizedValue;
}
} else if (!normalizedValue) {
delete nextParts.entity;
} else {
nextParts.entity = normalizedValue;
}
let nextTarget = this._cloneDeep(target);
nextTarget = this._deletePathValue(nextTarget, legacyEntityPath);
const legacyFixedValue = this._getPathValue(nextTarget, legacyFixedPath);
if (!this._isObject(legacyFixedValue)) {
nextTarget = this._deletePathValue(nextTarget, legacyFixedPath);
}
nextTarget = this._deletePathValue(nextTarget, canonicalBasePath);
const hasFixed = nextParts.fixed !== void 0 && nextParts.fixed !== null && nextParts.fixed !== "";
const hasEntity = nextParts.entity !== void 0 && nextParts.entity !== null && nextParts.entity !== "";
const hasPercent = Number.isFinite(nextParts.percent);
if (hasFixed || hasEntity || hasPercent) {
const nextValue = {};
if (hasFixed) nextValue.fixed = nextParts.fixed;
if (hasEntity) nextValue.entity = nextParts.entity;
if (hasPercent) nextValue.percent = nextParts.percent;
nextTarget = this._setPathValue(nextTarget, canonicalBasePath, nextValue);
}
prunePaths.forEach((path) => {
if (path.length) {
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, path);
}
});
return nextTarget;
}, options);
}
_clearCanonicalResolvableValue(scope, field, options = {}) {
var _a, _b, _c, _d;
const canonicalBasePath = (_a = options.canonicalBasePath) != null ? _a : ["scale", field];
const legacyFixedPath = (_b = options.legacyFixedPath) != null ? _b : [field];
const legacyEntityPath = (_c = options.legacyEntityPath) != null ? _c : [`${field}_entity`];
const prunePaths = (_d = options.prunePaths) != null ? _d : [canonicalBasePath, canonicalBasePath.slice(0, -1)];
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._deletePathValue(target, canonicalBasePath);
nextTarget = this._deletePathValue(nextTarget, legacyEntityPath);
const legacyFixedValue = this._getPathValue(nextTarget, legacyFixedPath);
if (!this._isObject(legacyFixedValue)) {
nextTarget = this._deletePathValue(nextTarget, legacyFixedPath);
}
prunePaths.forEach((path) => {
if (path.length) {
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, path);
}
});
return nextTarget;
}, options);
}
_getScopedDisplayValue(scope, canonicalPath, fallbackPaths = []) {
const valuesToTry = [canonicalPath, ...fallbackPaths];
for (const path of valuesToTry) {
const value = this._getScopedValue(scope, path);
if (value !== void 0 && value !== null && value !== "") {
return value;
}
}
return "";
}
_getEffectiveScopedDisplayValue(scope, canonicalPath, fallbackPaths = []) {
const valuesToTry = [canonicalPath, ...fallbackPaths];
for (const path of valuesToTry) {
const value = this._getScopedValue(scope, path);
if (value !== void 0 && value !== null && value !== "") {
return value;
}
}
if ((scope == null ? void 0 : scope.type) === "entity") {
for (const path of valuesToTry) {
const value = this._getScopedValue({ type: "card" }, path);
if (value !== void 0 && value !== null && value !== "") {
return value;
}
}
}
return "";
}
_getScopedFormattingValue(scope, key) {
var _a, _b;
return (_b = (_a = this._getScopedValue(scope, ["formatting", key])) != null ? _a : this._getScopedValue(scope, [key])) != null ? _b : "";
}
_getEffectiveScopedFormattingValue(scope, key) {
return this._getEffectiveScopedDisplayValue(scope, ["formatting", key], [[key]]);
}
_setScopedFormattingUnit(scope, rawValue) {
return this._setCanonicalScopedTextOverride(scope, ["formatting", "unit"], rawValue, {
deprecatedKeys: [["unit"]],
prunePaths: [["formatting"]]
});
}
_setScopedFormattingDecimal(scope, rawValue) {
const normalizedValue = this._normalizeDecimalValue(rawValue);
if (rawValue === "" || rawValue === null || rawValue === void 0) {
return this._removeCanonicalScopedValue(scope, ["formatting", "decimal"], {
deprecatedKeys: [["decimal"]],
prunePaths: [["formatting"]]
});
}
if (normalizedValue === null) {
return false;
}
return this._setCanonicalScopedValue(scope, ["formatting", "decimal"], normalizedValue, {
deprecatedKeys: [["decimal"]],
prunePaths: [["formatting"]]
});
}
_clearFormattingOverride(scope) {
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._deletePathValue(target, ["formatting", "unit"]);
nextTarget = this._deletePathValue(nextTarget, ["formatting", "decimal"]);
nextTarget = this._deletePathValue(nextTarget, ["unit"]);
nextTarget = this._deletePathValue(nextTarget, ["decimal"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["formatting"]);
return nextTarget;
}, { rerender: true });
}
_hasFormattingOverride(scope) {
var _a;
const formattingValue = (_a = this._getScopedValue(scope, ["formatting"])) != null ? _a : {};
if (this._isObject(formattingValue) && (Object.prototype.hasOwnProperty.call(formattingValue, "unit") || Object.prototype.hasOwnProperty.call(formattingValue, "decimal"))) {
return true;
}
return this._getScopedValue(scope, ["unit"]) !== void 0 || this._getScopedValue(scope, ["decimal"]) !== void 0;
}
_getScopedLayoutValue(scope, key) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
if (key === "height") {
return (_b = (_a = this._getScopedValue(scope, ["layout", "height"])) != null ? _a : this._getScopedValue(scope, ["height"])) != null ? _b : "";
}
if (key === "position") {
return (_d = (_c = this._getScopedValue(scope, ["layout", "label", "position"])) != null ? _c : this._getScopedValue(scope, ["label_position"])) != null ? _d : "";
}
if (key === "width") {
return (_f = (_e = this._getScopedValue(scope, ["layout", "label", "width"])) != null ? _e : this._getScopedValue(scope, ["label_width"])) != null ? _f : "";
}
if (key === "hero_size") {
return (_h = (_g = this._getScopedValue(scope, ["layout", "hero", "size"])) != null ? _g : this._getScopedValue(scope, ["layout", "label", "hero_size"])) != null ? _h : "";
}
if (key === "value_size") {
return (_i = this._getScopedValue(scope, ["layout", "hero", "value_size"])) != null ? _i : "";
}
return "";
}
_getEffectiveScopedLayoutValue(scope, key) {
if (key === "height") {
return this._getEffectiveScopedDisplayValue(scope, ["layout", "height"], [["height"]]);
}
if (key === "position") {
return this._getEffectiveScopedDisplayValue(scope, ["layout", "label", "position"], [["label_position"]]);
}
if (key === "width") {
return this._getEffectiveScopedDisplayValue(scope, ["layout", "label", "width"], [["label_width"]]);
}
if (key === "hero_size") {
return this._getEffectiveScopedDisplayValue(scope, ["layout", "hero", "size"], [["layout", "label", "hero_size"]]);
}
if (key === "value_size") {
return this._getEffectiveScopedDisplayValue(scope, ["layout", "hero", "value_size"]);
}
return "";
}
_setScopedLayoutLabelPosition(scope, value) {
if (!value) {
return this._removeCanonicalScopedValue(scope, ["layout", "label", "position"], {
deprecatedKeys: [["label_position"]],
prunePaths: [["layout", "label"], ["layout"]],
rerender: true
});
}
const didSet = this._setCanonicalScopedValue(scope, ["layout", "label", "position"], value, {
deprecatedKeys: [["label_position"]],
prunePaths: [["layout", "label"], ["layout"]],
rerender: true
});
if (!didSet || value === "hero") return didSet;
this._removeCanonicalScopedValue(scope, ["layout", "label", "hero_size"], {
prunePaths: [["layout", "label"], ["layout"]],
rerender: true
});
this._removeCanonicalScopedValue(scope, ["layout", "hero"], {
prunePaths: [["layout"]],
rerender: true
});
return true;
}
_setLayoutLabelPosition(value) {
return this._setScopedLayoutLabelPosition({ type: "card" }, value);
}
_setScopedLayoutHeroSize(scope, value) {
if (!value) {
return this._removeCanonicalScopedValue(scope, ["layout", "hero", "size"], {
deprecatedKeys: [["layout", "label", "hero_size"]],
prunePaths: [["layout", "hero"], ["layout"]]
});
}
return this._setCanonicalScopedValue(scope, ["layout", "hero", "size"], value, {
deprecatedKeys: [["layout", "label", "hero_size"]],
prunePaths: [["layout", "hero"], ["layout"]]
});
}
_setLayoutHeroSize(value) {
return this._setScopedLayoutHeroSize({ type: "card" }, value);
}
_normalizeHeroValueSizeValue(value) {
const numericValue = this._normalizeNumberValue(value);
if (numericValue === null) return null;
return Math.min(112, Math.max(12, numericValue));
}
_setScopedLayoutHeroValueSize(scope, value) {
if (value === "" || value === null || value === void 0) {
return this._removeCanonicalScopedValue(scope, ["layout", "hero", "value_size"], {
prunePaths: [["layout", "hero"], ["layout"]]
});
}
const valueSize = this._normalizeHeroValueSizeValue(value);
if (valueSize === null) return false;
return this._setCanonicalScopedValue(scope, ["layout", "hero", "value_size"], valueSize, {
prunePaths: [["layout", "hero"], ["layout"]]
});
}
_setLayoutHeroValueSize(value) {
return this._setScopedLayoutHeroValueSize({ type: "card" }, value);
}
_setScopedLayoutHeight(scope, value) {
const numericValue = this._normalizeNumberValue(value);
if (value === "" || value === null || value === void 0) {
return this._removeCanonicalScopedValue(scope, ["layout", "height"], {
deprecatedKeys: [["height"]],
prunePaths: [["layout"]]
});
}
if (numericValue === null || numericValue < 24) {
return false;
}
return this._setCanonicalScopedValue(scope, ["layout", "height"], numericValue, {
deprecatedKeys: [["height"]],
prunePaths: [["layout"]]
});
}
_setLayoutHeight(value) {
return this._setScopedLayoutHeight({ type: "card" }, value);
}
_setScopedLayoutLabelWidth(scope, value) {
const numericValue = this._normalizeNumberValue(value);
if (value === "" || value === null || value === void 0) {
return this._removeCanonicalScopedValue(scope, ["layout", "label", "width"], {
deprecatedKeys: [["label_width"]],
prunePaths: [["layout", "label"], ["layout"]]
});
}
if (numericValue === null) {
return false;
}
return this._setCanonicalScopedValue(scope, ["layout", "label", "width"], numericValue, {
deprecatedKeys: [["label_width"]],
prunePaths: [["layout", "label"], ["layout"]]
});
}
_clearLayoutOverride(scope) {
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._deletePathValue(target, ["layout", "height"]);
nextTarget = this._deletePathValue(nextTarget, ["layout", "label", "position"]);
nextTarget = this._deletePathValue(nextTarget, ["layout", "label", "hero_size"]);
nextTarget = this._deletePathValue(nextTarget, ["layout", "label", "width"]);
nextTarget = this._deletePathValue(nextTarget, ["layout", "hero"]);
nextTarget = this._deletePathValue(nextTarget, ["height"]);
nextTarget = this._deletePathValue(nextTarget, ["label_position"]);
nextTarget = this._deletePathValue(nextTarget, ["label_width"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["layout", "label"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["layout"]);
return nextTarget;
}, { rerender: true });
}
_hasLayoutOverride(scope) {
var _a, _b;
const layoutValue = (_a = this._getScopedValue(scope, ["layout"])) != null ? _a : {};
const labelValue = this._isObject(layoutValue) ? (_b = layoutValue.label) != null ? _b : {} : {};
if (this._isObject(layoutValue) && (Object.prototype.hasOwnProperty.call(layoutValue, "height") || this._isObject(labelValue) && (Object.prototype.hasOwnProperty.call(labelValue, "position") || Object.prototype.hasOwnProperty.call(labelValue, "hero_size") || Object.prototype.hasOwnProperty.call(labelValue, "width")))) {
return true;
}
if (this._isObject(layoutValue) && this._isObject(layoutValue.hero) && Object.keys(layoutValue.hero).length) {
return true;
}
return this._getScopedValue(scope, ["height"]) !== void 0 || this._getScopedValue(scope, ["label_position"]) !== void 0 || this._getScopedValue(scope, ["label_width"]) !== void 0;
}
_setScaleBound(key, value) {
return this._setCanonicalResolvablePart({ type: "card" }, key, "fixed", value);
}
_clearScaleOverride(scope) {
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._deletePathValue(target, ["scale", "min"]);
nextTarget = this._deletePathValue(nextTarget, ["scale", "max"]);
nextTarget = this._deletePathValue(nextTarget, ["min"]);
nextTarget = this._deletePathValue(nextTarget, ["max"]);
nextTarget = this._deletePathValue(nextTarget, ["min_entity"]);
nextTarget = this._deletePathValue(nextTarget, ["max_entity"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["scale"]);
return nextTarget;
}, { rerender: true });
}
_setBarFillStyle(value) {
return this._setScopedBarFillStyle({ type: "card" }, value);
}
_setBarColor(value) {
return this._setScopedBarColor({ type: "card" }, value);
}
_setGradientStops(stops, options = {}) {
return this._setScopedGradientStops({ type: "card" }, stops, options);
}
_setSegments(segments, options = {}) {
return this._setScopedSegments({ type: "card" }, segments, options);
}
_getDefaultGradientStops() {
return [
{ pos: 0, color: "#4CAF50" },
{ pos: 50, color: "#FF9800" },
{ pos: 100, color: "#F44336" }
];
}
_normalizeGradientStopPosValue(rawValue) {
const percent = parsePercentLiteral(rawValue);
const numericValue = Number.isFinite(percent) ? percent : this._normalizeNumberValue(rawValue);
if (numericValue === null || !Number.isFinite(numericValue)) {
return null;
}
if (numericValue < 0 || numericValue > 100) {
return null;
}
return numericValue;
}
_sanitizeGradientStopsForEmit(stops) {
if (!Array.isArray(stops)) {
return [];
}
return stops.map((stop) => {
if (!this._isObject(stop)) {
return null;
}
const pos = this._normalizeGradientStopPosValue(stop.pos);
const color = this._normalizeTextValue(stop.color).trim();
if (pos === null || !color) {
return null;
}
return {
...stop,
pos,
color
};
}).filter(Boolean).sort((left, right) => left.pos - right.pos);
}
_getGradientStopDraftColorDefault(scope = { type: "card" }) {
var _a;
const committedStops = this._sanitizeGradientStopsForEmit(this._getScopedGradientStopsValue(scope));
if (committedStops.length) {
return (_a = committedStops[committedStops.length - 1].color) != null ? _a : "#4CAF50";
}
return this._getDefaultGradientStops()[0].color;
}
_getNextSuggestedGradientStopPos(scope = { type: "card" }) {
const committedStops = this._sanitizeGradientStopsForEmit(this._getScopedGradientStopsValue(scope));
if (!committedStops.length) {
return 0;
}
const highest = committedStops[committedStops.length - 1];
if (highest.pos >= 100) {
return "";
}
let suggestedPos;
if (committedStops.length === 1) {
suggestedPos = highest.pos + 25;
} else {
const previous = committedStops[committedStops.length - 2];
suggestedPos = highest.pos + (highest.pos - previous.pos);
}
const clampedPos = Math.min(100, Math.max(0, suggestedPos));
if (committedStops.some((stop) => stop.pos === clampedPos)) {
return "";
}
return clampedPos;
}
_getGradientStopsDraftKey(scope) {
return (scope == null ? void 0 : scope.type) === "entity" ? `entity:${scope.index}` : "card";
}
_getGradientStopPosTextKey(scope, stopIndex) {
return `${this._getGradientStopsDraftKey(scope)}:pos:${stopIndex}`;
}
_getGradientStopPosText(scope = { type: "card" }, stopIndex, fallbackValue = "") {
const key = this._getGradientStopPosTextKey(scope, stopIndex);
if (this._gradientStopPosTexts.has(key)) {
return this._gradientStopPosTexts.get(key);
}
if (fallbackValue === "" || fallbackValue === null || fallbackValue === void 0) {
return "";
}
return String(fallbackValue);
}
_setGradientStopPosText(scope, stopIndex, rawValue) {
this._gradientStopPosTexts.set(
this._getGradientStopPosTextKey(scope, stopIndex),
this._normalizeTextValue(rawValue)
);
}
_clearGradientStopPosText(scope, stopIndex) {
this._gradientStopPosTexts.delete(this._getGradientStopPosTextKey(scope, stopIndex));
}
_clearGradientStopScopeTextState(scope) {
const prefix = `${this._getGradientStopsDraftKey(scope)}:pos:`;
for (const key of this._gradientStopPosTexts.keys()) {
if (key.startsWith(prefix)) {
this._gradientStopPosTexts.delete(key);
}
}
for (const key of this._gradientStopValidationMessages.keys()) {
if (key.startsWith(prefix)) {
this._gradientStopValidationMessages.delete(key);
}
}
}
_getGradientStopsUiRows(scope = { type: "card" }) {
const key = this._getGradientStopsDraftKey(scope);
if (this._gradientStopsUiRows.has(key)) {
return this._cloneDeep(this._gradientStopsUiRows.get(key));
}
return null;
}
_setGradientStopsUiRows(scope, stops) {
this._gradientStopsUiRows.set(this._getGradientStopsDraftKey(scope), this._cloneDeep(stops));
}
_getStoredScopedGradientStops(scope = { type: "card" }) {
const structuredValue = this._getScopedValue(scope, ["bar", "gradient_stops"]);
if (structuredValue !== void 0) {
return structuredValue;
}
const legacyValue = this._getScopedValue(scope, ["gradient_stops"]);
if (legacyValue !== void 0) {
return legacyValue;
}
return null;
}
_getFallbackGradientStops(scope = { type: "card" }) {
if ((scope == null ? void 0 : scope.type) === "entity") {
const inheritedStops = this._sanitizeGradientStopsForEmit(this._getScopedGradientStopsValue({ type: "card" }));
return inheritedStops.length ? inheritedStops : this._getDefaultGradientStops();
}
return this._getDefaultGradientStops();
}
_createGradientStopDraftState(scope = { type: "card" }) {
const suggestedPos = this._getNextSuggestedGradientStopPos(scope);
return {
pos: suggestedPos === "" ? "" : String(suggestedPos),
color: this._getGradientStopDraftColorDefault(scope)
};
}
_getGradientStopsDraftState(scope = { type: "card" }) {
const key = this._getGradientStopsDraftKey(scope);
if (!this._gradientStopsDrafts.has(key)) {
this._gradientStopsDrafts.set(key, this._createGradientStopDraftState(scope));
}
return this._cloneDeep(this._gradientStopsDrafts.get(key));
}
_setGradientStopsDraftState(scope, nextDraft, options = {}) {
var _a, _b;
this._gradientStopsDrafts.set(this._getGradientStopsDraftKey(scope), {
pos: (_a = nextDraft == null ? void 0 : nextDraft.pos) != null ? _a : "",
color: (_b = nextDraft == null ? void 0 : nextDraft.color) != null ? _b : this._getGradientStopDraftColorDefault(scope)
});
if (options == null ? void 0 : options.refreshOnly) {
this._refreshGradientDraftUi(scope);
return;
}
this._render();
}
_setGradientStopsDraftField(scope, field, rawValue) {
const currentDraft = this._getGradientStopsDraftState(scope);
const nextValue = field === "color" ? this._normalizeTextValue(rawValue).trim() : this._normalizeTextValue(rawValue);
this._setGradientStopsDraftState(scope, {
...currentDraft,
[field]: nextValue
}, { refreshOnly: field === "pos" });
}
_getValidGradientDraftStop(scope = { type: "card" }) {
const draft = this._getGradientStopsDraftState(scope);
const pos = this._normalizeGradientStopPosValue(draft.pos);
const color = this._normalizeTextValue(draft.color).trim();
if (pos === null || !color) {
return null;
}
return { pos, color };
}
_hasGradientStopDuplicate(scope = { type: "card" }, candidatePos, excludeIndex = null) {
return this._sanitizeGradientStopsForEmit(this._getScopedGradientStopsValue(scope)).some((stop, index) => index !== excludeIndex && stop.pos === candidatePos);
}
_canAddGradientStop(scope = { type: "card" }) {
const draftStop = this._getValidGradientDraftStop(scope);
if (!draftStop) {
return false;
}
return !this._hasGradientStopDuplicate(scope, draftStop.pos);
}
_getGradientDraftValidationMessage(scope = { type: "card" }) {
const draft = this._getGradientStopsDraftState(scope);
const normalizedPosText = this._normalizeTextValue(draft.pos).trim();
const normalizedColor = this._normalizeTextValue(draft.color).trim();
if (!normalizedPosText) {
return "Enter a position to add a stop.";
}
if (this._normalizeGradientStopPosValue(draft.pos) === null) {
return "Enter a value from 0 to 100.";
}
if (!normalizedColor) {
return "Choose a color to add a stop.";
}
if (this._hasGradientStopDuplicate(scope, this._normalizeGradientStopPosValue(draft.pos))) {
return "Position already exists.";
}
return "";
}
_isDefaultGradientStops(stops) {
const sanitizedStops = this._sanitizeGradientStopsForEmit(stops);
const defaultStops = this._getDefaultGradientStops();
if (sanitizedStops.length !== defaultStops.length) {
return false;
}
return sanitizedStops.every((stop, index) => stop.pos === defaultStops[index].pos && this._normalizeColorComparisonValue(stop.color) === this._normalizeColorComparisonValue(defaultStops[index].color));
}
_setScopedGradientStops(scope, stops, options = {}) {
var _a;
const sanitizedStops = this._sanitizeGradientStopsForEmit(stops);
const shouldRemove = sanitizedStops.length < 2 || this._isDefaultGradientStops(sanitizedStops);
const previousUiRowsJson = this._serializeConfig((_a = this._getGradientStopsUiRows(scope)) != null ? _a : []);
this._clearGradientStopScopeTextState(scope);
this._setGradientStopsUiRows(scope, sanitizedStops);
const applied = this._applyScopedMutation(scope, (target) => {
let nextTarget = this._deletePathValue(target, ["gradient_stops"]);
nextTarget = this._deletePathValue(nextTarget, ["bar", "gradient_stops"]);
if (!shouldRemove) {
nextTarget = this._setPathValue(nextTarget, ["bar", "gradient_stops"], sanitizedStops);
}
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["bar"]);
return nextTarget;
}, options);
if (applied === false && (options == null ? void 0 : options.rerender) && this._serializeConfig(sanitizedStops) !== previousUiRowsJson) {
this._render();
}
if (applied !== false && !(options == null ? void 0 : options.rerender)) {
this._refreshGradientDraftUi(scope);
}
return applied;
}
_clearGradientStopsOverride(scope) {
var _a;
const previousUiRowsJson = this._serializeConfig((_a = this._getGradientStopsUiRows(scope)) != null ? _a : []);
this._gradientStopsDrafts.delete(this._getGradientStopsDraftKey(scope));
this._gradientStopsUiRows.delete(this._getGradientStopsDraftKey(scope));
this._clearGradientStopScopeTextState(scope);
const applied = this._applyScopedMutation(scope, (target) => {
let nextTarget = this._deletePathValue(target, ["bar", "gradient_stops"]);
nextTarget = this._deletePathValue(nextTarget, ["gradient_stops"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["bar"]);
return nextTarget;
}, { rerender: true });
if (applied === false && previousUiRowsJson !== this._serializeConfig([])) {
this._render();
}
return applied;
}
_setScopedSegments(scope, segments, options = {}) {
const nextSegments = (options == null ? void 0 : options.sort) === false ? this._cloneDeep(segments) : this._sortSegmentsForEditor(segments);
const fallbackSegments = this._getFallbackSegments(scope);
const shouldRemove = !Array.isArray(nextSegments) || !nextSegments.length || fallbackSegments.length > 0 && this._segmentsEqualForEditor(nextSegments, fallbackSegments);
this._setSegmentsUiRows(scope, nextSegments);
const applied = this._applyScopedMutation(scope, (target) => {
let nextTarget = this._deletePathValue(target, ["bar", "segments"]);
nextTarget = this._deletePathValue(nextTarget, ["segments"]);
nextTarget = this._deletePathValue(nextTarget, ["severity"]);
if (!shouldRemove) {
nextTarget = this._setPathValue(nextTarget, ["bar", "segments"], nextSegments);
}
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["bar"]);
return nextTarget;
}, options);
if (applied !== false) {
this._refreshSegmentUi(scope);
}
return applied;
}
_clearSegmentsOverride(scope) {
this._segmentDrafts.delete(this._getSegmentsScopeKey(scope));
this._segmentUiRows.delete(this._getSegmentsScopeKey(scope));
this._clearSegmentScopeTextState(scope);
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._deletePathValue(target, ["bar", "segments"]);
nextTarget = this._deletePathValue(nextTarget, ["segments"]);
nextTarget = this._deletePathValue(nextTarget, ["severity"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["bar"]);
return nextTarget;
}, { rerender: true });
}
_setNeedle(value) {
return this._setScopedNeedleMode({ type: "card" }, value ? "enabled" : "disabled");
}
_getScopedPeakConfig(scope) {
var _a, _b;
const rawPeak = this._getScopedValue(scope, ["peak"]);
const rawPeakMarker = this._getScopedValue(scope, ["peak_marker"]);
const rawLegacyShow = this._getScopedValue(scope, ["show_peak"]);
const rawLegacyColor = this._getScopedValue(scope, ["peak_color"]);
const defaultColor = "#888";
let mode = (scope == null ? void 0 : scope.type) === "entity" ? "inherit" : "disabled";
let color = "";
if (this._isObject(rawPeak)) {
if (rawPeak.enabled === true) {
mode = "enabled";
} else if (rawPeak.enabled === false) {
mode = "disabled";
}
color = (_a = rawPeak.color) != null ? _a : color;
}
if (this._isObject(rawPeakMarker)) {
if (rawPeakMarker.show === true) {
mode = "enabled";
} else if (rawPeakMarker.show === false) {
mode = "disabled";
} else if ((scope == null ? void 0 : scope.type) !== "entity") {
mode = "disabled";
}
color = (_b = rawPeakMarker.color) != null ? _b : color;
}
if (rawLegacyShow === true) {
mode = "enabled";
} else if (rawLegacyShow === false) {
mode = "disabled";
}
color = color || rawLegacyColor || "";
if (color && this._normalizeColorComparisonValue(color) === this._normalizeColorComparisonValue(defaultColor)) {
color = "";
}
return { mode, color };
}
_getEffectiveScopedPeakConfig(scope) {
const localPeak = this._getScopedPeakConfig(scope);
if ((scope == null ? void 0 : scope.type) !== "entity") {
return localPeak;
}
if (!this._hasPeakOverride(scope)) {
return this._getScopedPeakConfig({ type: "card" });
}
const inheritedPeak = this._getScopedPeakConfig({ type: "card" });
return {
mode: localPeak.mode === "inherit" ? inheritedPeak.mode : localPeak.mode,
color: localPeak.color || inheritedPeak.color
};
}
_hasPeakOverride(scope) {
var _a, _b;
const peakValue = (_a = this._getScopedValue(scope, ["peak"])) != null ? _a : {};
if (this._isObject(peakValue) && (Object.prototype.hasOwnProperty.call(peakValue, "enabled") || Object.prototype.hasOwnProperty.call(peakValue, "color") || Object.prototype.hasOwnProperty.call(peakValue, "reset") || Object.prototype.hasOwnProperty.call(peakValue, "label") || Object.prototype.hasOwnProperty.call(peakValue, "direction"))) {
return true;
}
const peakMarkerValue = (_b = this._getScopedValue(scope, ["peak_marker"])) != null ? _b : {};
if (this._isObject(peakMarkerValue) && (Object.prototype.hasOwnProperty.call(peakMarkerValue, "show") || Object.prototype.hasOwnProperty.call(peakMarkerValue, "color") || Object.prototype.hasOwnProperty.call(peakMarkerValue, "direction"))) {
return true;
}
return this._getScopedValue(scope, ["show_peak"]) !== void 0 || this._getScopedValue(scope, ["peak_color"]) !== void 0;
}
_getPeakSummary(scope) {
if ((scope == null ? void 0 : scope.type) === "entity" && !this._hasPeakOverride(scope)) return "Inherited";
const peak = this._getScopedPeakConfig(scope);
if (peak.mode === "disabled") return peak.color ? "Disabled \u2022 Custom color" : "Disabled";
if (peak.mode === "enabled") return peak.color ? "Enabled \u2022 Custom color" : "Enabled";
if (peak.color) return "Custom color";
return "Inherited";
}
_clearPeakOverride(scope) {
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._deletePathValue(target, ["peak", "enabled"]);
nextTarget = this._deletePathValue(nextTarget, ["peak", "color"]);
nextTarget = this._deletePathValue(nextTarget, ["peak", "reset"]);
nextTarget = this._deletePathValue(nextTarget, ["peak", "label"]);
nextTarget = this._deletePathValue(nextTarget, ["peak", "direction"]);
nextTarget = this._deletePathValue(nextTarget, ["show_peak"]);
nextTarget = this._deletePathValue(nextTarget, ["peak_color"]);
nextTarget = this._deletePathValue(nextTarget, ["peak_marker"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["peak"]);
return nextTarget;
}, { rerender: true });
}
_setScopedPeakEnabled(scope, value) {
const boolValue = !!value;
const defaultColor = "#888";
return this._applyScopedMutation(scope, (target) => {
var _a, _b, _c;
let nextTarget = this._cloneDeep(target);
const currentPeak = this._isObject(this._getPathValue(nextTarget, ["peak"])) ? this._cloneDeep(this._getPathValue(nextTarget, ["peak"])) : {};
const currentColor = (_c = (_b = (_a = currentPeak.color) != null ? _a : this._isObject(this._getPathValue(nextTarget, ["peak_marker"])) ? this._getPathValue(nextTarget, ["peak_marker", "color"]) : void 0) != null ? _b : this._getPathValue(nextTarget, ["peak_color"])) != null ? _c : defaultColor;
if ((scope == null ? void 0 : scope.type) === "entity" || boolValue) {
currentPeak.enabled = boolValue;
} else {
delete currentPeak.enabled;
}
if (currentColor && this._normalizeColorComparisonValue(currentColor) !== this._normalizeColorComparisonValue(defaultColor)) {
currentPeak.color = currentColor;
} else {
delete currentPeak.color;
}
if (Object.keys(currentPeak).length) {
nextTarget = this._setPathValue(nextTarget, ["peak"], currentPeak);
} else {
nextTarget = this._deletePathValue(nextTarget, ["peak"]);
}
nextTarget = this._deletePathValue(nextTarget, ["show_peak"]);
nextTarget = this._deletePathValue(nextTarget, ["peak_color"]);
nextTarget = this._deletePathValue(nextTarget, ["peak_marker"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["peak"]);
return nextTarget;
});
}
_setScopedPeakColor(scope, rawValue) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
const defaultColor = "#888";
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._cloneDeep(target);
const currentPeak = this._isObject(this._getPathValue(nextTarget, ["peak"])) ? this._cloneDeep(this._getPathValue(nextTarget, ["peak"])) : {};
const currentConfig = this._getScopedPeakConfig(scope);
delete currentPeak.color;
if (normalizedValue && this._normalizeColorComparisonValue(normalizedValue) !== this._normalizeColorComparisonValue(defaultColor)) {
currentPeak.color = normalizedValue;
}
if ((scope == null ? void 0 : scope.type) === "entity") {
if (currentConfig.mode === "enabled") currentPeak.enabled = true;
if (currentConfig.mode === "disabled") currentPeak.enabled = false;
} else if (currentConfig.mode === "enabled") {
currentPeak.enabled = true;
}
if (Object.keys(currentPeak).length) {
nextTarget = this._setPathValue(nextTarget, ["peak"], currentPeak);
} else {
nextTarget = this._deletePathValue(nextTarget, ["peak"]);
}
nextTarget = this._deletePathValue(nextTarget, ["show_peak"]);
nextTarget = this._deletePathValue(nextTarget, ["peak_color"]);
nextTarget = this._deletePathValue(nextTarget, ["peak_marker"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["peak"]);
return nextTarget;
});
}
_getScopedMarkerExtras(scope, key) {
var _a, _b, _c, _d;
const raw = this._getScopedValue(scope, [key]);
const marker = this._isObject(raw) ? raw : {};
const label = this._isObject(marker.label) ? marker.label : {};
return {
reset: Object.prototype.hasOwnProperty.call(marker, "reset") ? this._normalizeTextValue(marker.reset).trim().toLowerCase() : null,
labelShow: typeof label.show === "boolean" ? label.show : null,
labelText: typeof label.text === "string" ? label.text.replace(/\s+/g, " ").trim() : null,
labelShowValue: typeof label.show_value === "boolean" ? label.show_value : null,
labelShowUnit: typeof label.show_unit === "boolean" ? label.show_unit : null,
labelPrecision: ((_a = label.precision) != null ? _a : label.decimal) === void 0 || ((_b = label.precision) != null ? _b : label.decimal) === null || ((_c = label.precision) != null ? _c : label.decimal) === "" ? null : this._normalizeNumberValue((_d = label.precision) != null ? _d : label.decimal)
};
}
_hasExtremumOverride(scope, key) {
const marker = this._getScopedValue(scope, [key]);
if (!this._isObject(marker)) return false;
return ["enabled", "color", "reset", "label", "direction"].some((field) => Object.prototype.hasOwnProperty.call(marker, field));
}
_getEffectiveMarkerExtras(scope, key) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
const local = this._getScopedMarkerExtras(scope, key);
if ((scope == null ? void 0 : scope.type) !== "entity") {
return {
reset: (_a = local.reset) != null ? _a : "never",
labelShow: (_b = local.labelShow) != null ? _b : false,
labelText: local.labelText,
labelShowValue: (_c = local.labelShowValue) != null ? _c : true,
labelShowUnit: (_d = local.labelShowUnit) != null ? _d : true,
labelPrecision: local.labelPrecision
};
}
const card = this._getScopedMarkerExtras({ type: "card" }, key);
if (!this._hasExtremumOverride(scope, key)) {
return {
reset: (_e = card.reset) != null ? _e : "never",
labelShow: (_f = card.labelShow) != null ? _f : false,
labelText: card.labelText,
labelShowValue: (_g = card.labelShowValue) != null ? _g : true,
labelShowUnit: (_h = card.labelShowUnit) != null ? _h : true,
labelPrecision: card.labelPrecision
};
}
return {
reset: (_j = (_i = local.reset) != null ? _i : card.reset) != null ? _j : "never",
labelShow: (_l = (_k = local.labelShow) != null ? _k : card.labelShow) != null ? _l : false,
labelText: Object.prototype.hasOwnProperty.call((_m = this._getScopedValue(scope, [key, "label"])) != null ? _m : {}, "text") ? local.labelText : card.labelText,
labelShowValue: (_o = (_n = local.labelShowValue) != null ? _n : card.labelShowValue) != null ? _o : true,
labelShowUnit: (_q = (_p = local.labelShowUnit) != null ? _p : card.labelShowUnit) != null ? _q : true,
labelPrecision: (_r = local.labelPrecision) != null ? _r : card.labelPrecision
};
}
_getScopedFloorConfig(scope) {
const raw = this._getScopedValue(scope, ["floor"]);
const marker = this._isObject(raw) ? raw : {};
let mode = (scope == null ? void 0 : scope.type) === "entity" ? "inherit" : "disabled";
if (marker.enabled === true) mode = "enabled";
if (marker.enabled === false) mode = "disabled";
const color = marker.color && this._normalizeColorComparisonValue(marker.color) !== this._normalizeColorComparisonValue("#888888") ? marker.color : "";
return { mode, color };
}
_getEffectiveScopedFloorConfig(scope) {
const local = this._getScopedFloorConfig(scope);
const extras = this._getEffectiveMarkerExtras(scope, "floor");
if ((scope == null ? void 0 : scope.type) !== "entity") return { ...local, ...extras };
if (!this._hasExtremumOverride(scope, "floor")) {
return { ...this._getScopedFloorConfig({ type: "card" }), ...extras };
}
const card = this._getEffectiveScopedFloorConfig({ type: "card" });
return {
mode: local.mode === "inherit" ? card.mode : local.mode,
color: local.color || card.color,
...extras
};
}
_getFloorSummary(scope) {
if ((scope == null ? void 0 : scope.type) === "entity" && !this._hasExtremumOverride(scope, "floor")) return "Inherited";
const floor = this._getEffectiveScopedFloorConfig(scope);
if (floor.mode === "enabled") return floor.color ? "Enabled \u2022 Custom color" : "Enabled";
if (floor.color) return "Disabled \u2022 Custom color";
return "Disabled";
}
_getMarkerResetSummary(key) {
var _a;
const scope = { type: "card" };
const marker = key === "peak" ? this._getScopedPeakConfig(scope) : this._getEffectiveScopedFloorConfig(scope);
const enabled = marker.mode === "enabled";
const reset = (_a = this._getEffectiveMarkerExtras(scope, key).reset) != null ? _a : "never";
return `${enabled ? "Enabled" : "Disabled"} \xB7 ${reset === "never" ? "no reset" : `${reset} reset`}`;
}
_getCardTargetMarkerSummary() {
const mode = this._getTargetMode({ type: "card" });
if (mode === "disabled") return "Disabled";
const target = this._getTargetResolvableValue({ type: "card" });
const parts = [];
if (mode === "enabled") parts.push("Enabled");
const hasTargetValue = target.fixed !== "" && target.fixed !== void 0 || !!target.entity;
if (target.fixed !== "" && target.fixed !== void 0) parts.push(String(target.fixed));
if (target.entity) parts.push(target.entity);
if (hasTargetValue || this._hasTargetShape({ type: "card" })) {
parts.push(this._getEffectiveTargetShapeValue({ type: "card" }) === "triangle" ? "Triangle" : "Diamond");
}
return parts.length ? parts.join(" \xB7 ") : "Automatic";
}
_setScopedExtremumEnabled(scope, key, value) {
const boolValue = !!value;
const defaultColor = "#888888";
return this._applyScopedMutation(scope, (target) => {
var _a;
let nextTarget = this._cloneDeep(target);
const current = this._isObject(this._getPathValue(nextTarget, [key])) ? this._cloneDeep(this._getPathValue(nextTarget, [key])) : {};
const currentColor = (_a = current.color) != null ? _a : defaultColor;
if ((scope == null ? void 0 : scope.type) === "entity" || boolValue) current.enabled = boolValue;
else delete current.enabled;
if (currentColor && this._normalizeColorComparisonValue(currentColor) !== this._normalizeColorComparisonValue(defaultColor)) {
current.color = currentColor;
} else delete current.color;
if (Object.keys(current).length) nextTarget = this._setPathValue(nextTarget, [key], current);
else nextTarget = this._deletePathValue(nextTarget, [key]);
return nextTarget;
});
}
_setScopedExtremumColor(scope, key, rawValue) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
const defaultColor = "#888888";
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._cloneDeep(target);
const current = this._isObject(this._getPathValue(nextTarget, [key])) ? this._cloneDeep(this._getPathValue(nextTarget, [key])) : {};
delete current.color;
if (normalizedValue && this._normalizeColorComparisonValue(normalizedValue) !== this._normalizeColorComparisonValue(defaultColor)) {
current.color = normalizedValue;
}
const mode = this._getScopedFloorConfig(scope).mode;
if (key === "floor" && ((scope == null ? void 0 : scope.type) === "entity" && mode !== "inherit" || (scope == null ? void 0 : scope.type) !== "entity" && mode === "enabled")) {
current.enabled = mode === "enabled";
}
if (Object.keys(current).length) nextTarget = this._setPathValue(nextTarget, [key], current);
else nextTarget = this._deletePathValue(nextTarget, [key]);
if (key === "peak") {
nextTarget = this._deletePathValue(nextTarget, ["show_peak"]);
nextTarget = this._deletePathValue(nextTarget, ["peak_color"]);
nextTarget = this._deletePathValue(nextTarget, ["peak_marker"]);
}
return nextTarget;
});
}
_setScopedExtremumReset(scope, key, value) {
const normalized = this._normalizeTextValue(value).trim().toLowerCase();
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._cloneDeep(target);
const current = this._isObject(this._getPathValue(nextTarget, [key])) ? this._cloneDeep(this._getPathValue(nextTarget, [key])) : {};
if (normalized && ((scope == null ? void 0 : scope.type) === "entity" || normalized !== "never")) current.reset = normalized;
else delete current.reset;
if (Object.keys(current).length) nextTarget = this._setPathValue(nextTarget, [key], current);
else nextTarget = this._deletePathValue(nextTarget, [key]);
if (key === "peak") {
nextTarget = this._deletePathValue(nextTarget, ["show_peak"]);
nextTarget = this._deletePathValue(nextTarget, ["peak_color"]);
nextTarget = this._deletePathValue(nextTarget, ["peak_marker"]);
}
return nextTarget;
});
}
_setScopedExtremumLabelShow(scope, key, value) {
const enabled = !!value;
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._cloneDeep(target);
const current = this._isObject(this._getPathValue(nextTarget, [key])) ? this._cloneDeep(this._getPathValue(nextTarget, [key])) : {};
const label = this._isObject(current.label) ? this._cloneDeep(current.label) : {};
if ((scope == null ? void 0 : scope.type) === "entity" || enabled) label.show = enabled;
else delete label.show;
if (Object.keys(label).length) current.label = label;
else delete current.label;
if (Object.keys(current).length) nextTarget = this._setPathValue(nextTarget, [key], current);
else nextTarget = this._deletePathValue(nextTarget, [key]);
if (key === "peak") {
nextTarget = this._deletePathValue(nextTarget, ["show_peak"]);
nextTarget = this._deletePathValue(nextTarget, ["peak_color"]);
nextTarget = this._deletePathValue(nextTarget, ["peak_marker"]);
}
return nextTarget;
});
}
_setScopedExtremumLabelDecimal(scope, key, value) {
const decimal = this._normalizeNumberValue(value);
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._cloneDeep(target);
const current = this._isObject(this._getPathValue(nextTarget, [key])) ? this._cloneDeep(this._getPathValue(nextTarget, [key])) : {};
const label = this._isObject(current.label) ? this._cloneDeep(current.label) : {};
if (decimal !== null) label.decimal = decimal;
else delete label.decimal;
if (Object.keys(label).length) current.label = label;
else delete current.label;
if (Object.keys(current).length) nextTarget = this._setPathValue(nextTarget, [key], current);
else nextTarget = this._deletePathValue(nextTarget, [key]);
return nextTarget;
});
}
_getBuiltinMarkerLabelOptions(scope, key) {
var _a;
const marker = this._getScopedValue(scope, [key]);
const label = this._isObject(marker == null ? void 0 : marker.label) ? marker.label : {};
const cardLabel = (scope == null ? void 0 : scope.type) === "entity" ? (_a = this._getScopedValue({ type: "card" }, [key, "label"])) != null ? _a : {} : {};
const own = (name) => Object.prototype.hasOwnProperty.call(label, name);
const inheritedValue = (name, fallback) => {
var _a2;
return own(name) ? label[name] : (_a2 = cardLabel[name]) != null ? _a2 : fallback;
};
const precision = inheritedValue("precision", inheritedValue("decimal", ""));
return {
show: key === "target" ? this._getEffectiveTargetLabelShowValue(scope) : this._getEffectiveMarkerExtras(scope, key).labelShow,
text: inheritedValue("text", ""),
showValue: inheritedValue("show_value", true) !== false,
showUnit: inheritedValue("show_unit", true) !== false,
precision: precision === null ? "" : precision
};
}
_renderBuiltinMarkerLabelControls(scope, key, title) {
var _a;
const options = this._getBuiltinMarkerLabelOptions(scope, key);
const isEntity = (scope == null ? void 0 : scope.type) === "entity";
const prefix = isEntity ? `entity-${scope.index}-${key}` : key;
const attr = isEntity ? `data-kind="entity-${key}-label-` : `data-field="${key}-label-`;
const suffix = isEntity ? `" data-index="${scope.index}"` : '"';
return `
<div class="field-row"><div class="toggle">
<input id="${prefix}-label-show" type="checkbox" ${attr}show${suffix}${options.show ? " checked" : ""}>
<label for="${prefix}-label-show">Show ${title} label</label>
</div></div>
<div class="field-row"><label for="${prefix}-label-text">${title} label text</label>
<input id="${prefix}-label-text" type="text" ${attr}text${suffix} value="${this._escapeAttribute((_a = options.text) != null ? _a : "")}" placeholder="optional semantic text">
</div>
<div class="field-row"><div class="toggle">
<input id="${prefix}-label-show-value" type="checkbox" ${attr}show-value${suffix}${options.showValue ? " checked" : ""}>
<label for="${prefix}-label-show-value">Show value</label>
</div></div>
<div class="field-row"><div class="toggle">
<input id="${prefix}-label-show-unit" type="checkbox" ${attr}show-unit${suffix}${options.showUnit ? " checked" : ""}>
<label for="${prefix}-label-show-unit">Show unit</label>
</div></div>
<div class="field-row"><label for="${prefix}-label-precision">${title} label precision</label>
<input id="${prefix}-label-precision" type="number" min="0" step="1" ${attr}precision${suffix} value="${this._escapeAttribute(options.precision)}" placeholder="inherit primary precision">
</div>`;
}
_setBuiltinMarkerLabelField(scope, key, field, value) {
const publicField = field === "showValue" ? "show_value" : field === "showUnit" ? "show_unit" : field === "precision" ? "precision" : field;
const isText = field === "text";
const normalized = isText ? this._normalizeTextValue(value).replace(/\s+/g, " ").trim() : field === "show" || field === "showValue" || field === "showUnit" ? value === true : this._normalizeDecimalValue(value);
if (field === "precision" && value !== "" && normalized === null) return false;
return this._applyScopedMutation(scope, (target) => {
var _a, _b, _c, _d;
let next = this._cloneDeep(target);
if (key === "target" && field === "show") next = this._deletePathValue(next, ["show_target_label"]);
const marker = this._isObject(this._getPathValue(next, [key])) ? this._cloneDeep(this._getPathValue(next, [key])) : {};
const label = this._isObject(marker.label) ? this._cloneDeep(marker.label) : {};
const inheritedLabel = (scope == null ? void 0 : scope.type) === "entity" ? (_a = this._getScopedValue({ type: "card" }, [key, "label"])) != null ? _a : {} : {};
const inheritedValue = field === "precision" ? (_c = (_b = inheritedLabel.precision) != null ? _b : inheritedLabel.decimal) != null ? _c : null : (_d = inheritedLabel[publicField]) != null ? _d : field === "show" ? false : field === "showValue" || field === "showUnit" ? true : void 0;
if (normalized === null || isText && !normalized) {
if (isText && (scope == null ? void 0 : scope.type) === "entity" && typeof inheritedLabel.text === "string" && inheritedLabel.text.trim()) {
label[publicField] = "";
} else {
delete label[publicField];
}
} else if ((scope == null ? void 0 : scope.type) === "entity" && normalized === inheritedValue) {
delete label[publicField];
} else if ((field === "show" || field === "showValue" || field === "showUnit") && (scope == null ? void 0 : scope.type) !== "entity" && normalized === (field === "show" ? false : true)) {
delete label[publicField];
} else {
label[publicField] = normalized;
}
if (field === "precision") delete label.decimal;
if (Object.keys(label).length) marker.label = label;
else delete marker.label;
if (Object.keys(marker).length) next = this._setPathValue(next, [key], marker);
else next = this._deletePathValue(next, [key]);
return next;
});
}
_clearFloorOverride(scope) {
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._cloneDeep(target);
const floor = this._isObject(this._getPathValue(nextTarget, ["floor"])) ? this._cloneDeep(this._getPathValue(nextTarget, ["floor"])) : {};
["enabled", "color", "reset", "label", "direction"].forEach((key) => delete floor[key]);
if (Object.keys(floor).length) nextTarget = this._setPathValue(nextTarget, ["floor"], floor);
else nextTarget = this._deletePathValue(nextTarget, ["floor"]);
nextTarget = this._deletePathValue(nextTarget, ["floor_marker"]);
return nextTarget;
}, { rerender: true });
}
_setFixedMarkerValue(rootKey, enabled, value) {
const numericValue = this._normalizeNumberValue(value);
if (!enabled || numericValue === null) {
return this._removeCanonicalScopedValue({ type: "card" }, [rootKey, "at", "fixed"], {
deprecatedKeys: rootKey === "target" ? [["target_entity"]] : [],
prunePaths: [[rootKey, "at"], [rootKey]]
});
}
return this._setCanonicalScopedValue({ type: "card" }, [rootKey, "at", "fixed"], numericValue, {
deprecatedKeys: rootKey === "target" ? [["target_entity"]] : [],
prunePaths: [[rootKey, "at"], [rootKey]]
});
}
_setPeakShow(value) {
return this._setScopedPeakEnabled({ type: "card" }, value);
}
_readFixedMarker(rootKey) {
const rawValue = this._draftConfig[rootKey];
if (this._isObject(rawValue)) {
const fixed = this._isObject(rawValue == null ? void 0 : rawValue.at) ? rawValue.at.fixed : rawValue == null ? void 0 : rawValue.at;
return {
enabled: fixed !== void 0 && fixed !== null && fixed !== "",
value: fixed != null ? fixed : ""
};
}
return {
enabled: rawValue !== void 0 && rawValue !== null && rawValue !== "",
value: rawValue != null ? rawValue : ""
};
}
_getGradientStopsValue() {
return this._getScopedGradientStopsValue({ type: "card" });
}
_getSegmentsValue() {
return this._getScopedSegmentsValue({ type: "card" });
}
_getSegmentsScopeKey(scope = { type: "card" }) {
return (scope == null ? void 0 : scope.type) === "entity" ? `entity:${scope.index}` : "card";
}
_getSegmentBoundaryTextKey(scope, segmentIndex, field) {
return `${this._getSegmentsScopeKey(scope)}:${segmentIndex}:${field}`;
}
_getSegmentBoundaryText(scope = { type: "card" }, segmentIndex, field, fallbackValue = "") {
const key = this._getSegmentBoundaryTextKey(scope, segmentIndex, field);
if (this._segmentBoundaryTexts.has(key)) {
return this._segmentBoundaryTexts.get(key);
}
return this._formatSegmentBoundaryValue(fallbackValue);
}
_setSegmentBoundaryText(scope, segmentIndex, field, rawValue) {
this._segmentBoundaryTexts.set(
this._getSegmentBoundaryTextKey(scope, segmentIndex, field),
this._normalizeTextValue(rawValue)
);
}
_clearSegmentBoundaryText(scope, segmentIndex, field) {
this._segmentBoundaryTexts.delete(this._getSegmentBoundaryTextKey(scope, segmentIndex, field));
}
_clearSegmentScopeTextState(scope) {
const prefix = `${this._getSegmentsScopeKey(scope)}:`;
for (const key of this._segmentBoundaryTexts.keys()) {
if (key.startsWith(prefix)) {
this._segmentBoundaryTexts.delete(key);
}
}
}
_getSegmentsUiRows(scope = { type: "card" }) {
const key = this._getSegmentsScopeKey(scope);
if (this._segmentUiRows.has(key)) {
return this._cloneDeep(this._segmentUiRows.get(key));
}
return null;
}
_setSegmentsUiRows(scope, rows) {
this._segmentUiRows.set(this._getSegmentsScopeKey(scope), this._cloneDeep(rows));
}
_getSegmentDraftState(scope = { type: "card" }) {
const key = this._getSegmentsScopeKey(scope);
if (!this._segmentDrafts.has(key)) {
this._segmentDrafts.set(key, this._createSegmentDraftState(scope));
}
return this._cloneDeep(this._segmentDrafts.get(key));
}
_setSegmentDraftState(scope, nextDraft, options = {}) {
var _a, _b, _c;
this._segmentDrafts.set(this._getSegmentsScopeKey(scope), {
from: (_a = nextDraft == null ? void 0 : nextDraft.from) != null ? _a : "",
to: (_b = nextDraft == null ? void 0 : nextDraft.to) != null ? _b : "",
color: (_c = nextDraft == null ? void 0 : nextDraft.color) != null ? _c : this._getSegmentDraftColorDefault(scope)
});
if (options == null ? void 0 : options.refreshOnly) {
this._refreshSegmentUi(scope);
return;
}
this._render();
}
_setSegmentDraftField(scope, field, rawValue) {
const currentDraft = this._getSegmentDraftState(scope);
const nextValue = field === "color" ? this._normalizeTextValue(rawValue).trim() : this._normalizeTextValue(rawValue);
this._setSegmentDraftState(scope, {
...currentDraft,
[field]: nextValue
}, { refreshOnly: true });
}
_isSegmentFillStyle(fillStyle) {
return ["bands", "soft_bands", "band_gradient"].includes(fillStyle);
}
_getDefaultSegments() {
return [
{ from: "0%", to: "33%", color: "#4CAF50" },
{ from: "33%", to: "75%", color: "#FF9800" },
{ from: "75%", to: "100%", color: "#F44336" }
];
}
_getStoredScopedSegments(scope = { type: "card" }) {
const structuredValue = this._getScopedValue(scope, ["bar", "segments"]);
if (structuredValue !== void 0) {
return structuredValue;
}
const legacySegments = this._getScopedValue(scope, ["segments"]);
if (legacySegments !== void 0) {
return legacySegments;
}
const legacySeverity = this._getScopedValue(scope, ["severity"]);
if (legacySeverity !== void 0) {
return legacySeverity;
}
return null;
}
_parseSegmentBoundaryInput(rawValue) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
if (!normalizedValue) {
return null;
}
const percentMatch = normalizedValue.match(/^\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*%\s*$/);
const percent = percentMatch ? parseFloat(percentMatch[1]) : null;
if (Number.isFinite(percent)) {
return `${percent}%`;
}
const numericValue = this._normalizeNumberValue(normalizedValue);
return numericValue === null ? null : numericValue;
}
_formatSegmentBoundaryValue(value) {
if (typeof value === "string") {
return value;
}
if (typeof value === "number" && Number.isFinite(value)) {
return String(value);
}
if (this._isObject(value)) {
if (Number.isFinite(value.percent)) {
return `${value.percent}%`;
}
if (Number.isFinite(this._getFiniteNumber(value.fixed))) {
return String(this._getFiniteNumber(value.fixed));
}
}
return "";
}
_getSegmentDraftColorDefault(scope = { type: "card" }) {
var _a;
const segments = this._getScopedSegmentsValue(scope);
if (segments.length) {
return this._normalizeTextValue((_a = segments[segments.length - 1]) == null ? void 0 : _a.color).trim() || "#4a9eff";
}
return "#4CAF50";
}
_getNewSegmentDefaults(scope = { type: "card" }) {
var _a;
const segments = this._getScopedSegmentsValue(scope);
const previous = segments[segments.length - 1];
const previousTo = (_a = previous == null ? void 0 : previous.to) != null ? _a : null;
return {
from: previousTo != null ? previousTo : "0%",
to: "100%",
color: "#4a9eff"
};
}
_createSegmentDraftState(scope = { type: "card" }) {
var _a;
const defaults = this._getNewSegmentDefaults(scope);
const formattedFrom = this._formatSegmentBoundaryValue(defaults.from);
const formattedTo = this._formatSegmentBoundaryValue(defaults.to);
const draftFrom = formattedFrom === "100%" || formattedFrom === "100" ? "" : formattedFrom;
const draftTo = draftFrom ? formattedTo : "";
return {
from: draftFrom,
to: draftTo,
color: (_a = defaults.color) != null ? _a : this._getSegmentDraftColorDefault(scope)
};
}
_normalizeSegmentForEditorComparison(segment) {
if (!this._isObject(segment)) {
return null;
}
const from = this._formatSegmentBoundaryValue(segment.from).trim();
const to = this._formatSegmentBoundaryValue(segment.to).trim();
const color = this._normalizeColorComparisonValue(segment.color);
if (!from || !to || !color) {
return null;
}
return { from, to, color };
}
_segmentsEqualForEditor(leftSegments, rightSegments) {
const left = Array.isArray(leftSegments) ? leftSegments.map((segment) => this._normalizeSegmentForEditorComparison(segment)).filter(Boolean) : [];
const right = Array.isArray(rightSegments) ? rightSegments.map((segment) => this._normalizeSegmentForEditorComparison(segment)).filter(Boolean) : [];
if (left.length !== right.length) {
return false;
}
return left.every((segment, index) => segment.from === right[index].from && segment.to === right[index].to && segment.color === right[index].color);
}
_getFallbackSegments(scope = { type: "card" }) {
if ((scope == null ? void 0 : scope.type) === "entity") {
const cardStoredSegments = this._getStoredScopedSegments({ type: "card" });
if (cardStoredSegments !== null) {
return this._cloneDeep(this._getScopedSegmentsValue({ type: "card" }));
}
}
if (!this._isSegmentFillStyle(this._getEffectiveFillStyleValue(scope))) {
return [];
}
return this._cloneDeep(this._getDefaultSegments());
}
_parseSegmentBoundaryText(rawValue) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
if (!normalizedValue) {
return { state: "empty", value: null };
}
const parsed = this._parseSegmentBoundaryInput(normalizedValue);
if (parsed === null) {
return { state: "invalid", value: null };
}
return { state: "valid", value: parsed };
}
_compareSegmentBoundaries(left, right) {
const leftValue = this._getSegmentPreviewBoundaryValue(left);
const rightValue = this._getSegmentPreviewBoundaryValue(right);
if (leftValue === null || rightValue === null) {
return null;
}
if (leftValue < rightValue) return -1;
if (leftValue > rightValue) return 1;
return 0;
}
_buildSegmentValidationRows(scope = { type: "card" }) {
var _a;
const rows = (_a = this._getSegmentsUiRows(scope)) != null ? _a : this._getScopedSegmentsValue(scope);
return rows.map((segment, index) => {
const rawFrom = this._getSegmentBoundaryText(scope, index, "from", segment == null ? void 0 : segment.from);
const rawTo = this._getSegmentBoundaryText(scope, index, "to", segment == null ? void 0 : segment.to);
const parsedFrom = this._parseSegmentBoundaryText(rawFrom);
const parsedTo = this._parseSegmentBoundaryText(rawTo);
return {
index,
rawFrom,
rawTo,
parsedFrom,
parsedTo
};
});
}
_getSegmentRowValidationMessage(scope = { type: "card" }, segmentIndex) {
const rows = this._buildSegmentValidationRows(scope);
const row = rows[segmentIndex];
if (!row) {
return "";
}
if (row.parsedFrom.state === "invalid" || row.parsedTo.state === "invalid" || row.parsedFrom.state === "empty" || row.parsedTo.state === "empty") {
return "Enter valid from/to values.";
}
if (this._compareSegmentBoundaries(row.parsedFrom.value, row.parsedTo.value) !== -1) {
return "From must be below To.";
}
const candidateFrom = this._getSegmentPreviewBoundaryValue(row.parsedFrom.value);
const candidateTo = this._getSegmentPreviewBoundaryValue(row.parsedTo.value);
for (const other of rows) {
if (other.index === segmentIndex) continue;
if (other.parsedFrom.state !== "valid" || other.parsedTo.state !== "valid") continue;
const otherFrom = this._getSegmentPreviewBoundaryValue(other.parsedFrom.value);
const otherTo = this._getSegmentPreviewBoundaryValue(other.parsedTo.value);
if (candidateFrom === otherFrom) {
return "Duplicate segment start.";
}
if (candidateFrom < otherTo && candidateTo > otherFrom) {
return "Segments overlap.";
}
}
return "";
}
_getValidSegmentDraft(scope = { type: "card" }) {
const draft = this._getSegmentDraftState(scope);
const parsedFrom = this._parseSegmentBoundaryText(draft.from);
const parsedTo = this._parseSegmentBoundaryText(draft.to);
const color = this._normalizeTextValue(draft.color).trim();
if (parsedFrom.state !== "valid" || parsedTo.state !== "valid" || !color) {
return null;
}
if (this._compareSegmentBoundaries(parsedFrom.value, parsedTo.value) !== -1) {
return null;
}
const candidateFrom = this._getSegmentPreviewBoundaryValue(parsedFrom.value);
const candidateTo = this._getSegmentPreviewBoundaryValue(parsedTo.value);
const rows = this._buildSegmentValidationRows(scope);
for (const row of rows) {
if (row.parsedFrom.state !== "valid" || row.parsedTo.state !== "valid") continue;
const otherFrom = this._getSegmentPreviewBoundaryValue(row.parsedFrom.value);
const otherTo = this._getSegmentPreviewBoundaryValue(row.parsedTo.value);
if (candidateFrom === otherFrom || candidateFrom < otherTo && candidateTo > otherFrom) {
return null;
}
}
return {
from: parsedFrom.value,
to: parsedTo.value,
color
};
}
_canAddSegment(scope = { type: "card" }) {
return !!this._getValidSegmentDraft(scope);
}
_getSegmentDraftValidationMessage(scope = { type: "card" }) {
const draft = this._getSegmentDraftState(scope);
const parsedFrom = this._parseSegmentBoundaryText(draft.from);
const parsedTo = this._parseSegmentBoundaryText(draft.to);
const color = this._normalizeTextValue(draft.color).trim();
if (!this._normalizeTextValue(draft.from).trim() && !this._normalizeTextValue(draft.to).trim()) {
return "";
}
if (parsedFrom.state !== "valid" || parsedTo.state !== "valid") {
return "Enter valid from/to values.";
}
if (this._compareSegmentBoundaries(parsedFrom.value, parsedTo.value) !== -1) {
return "From must be below To.";
}
if (!color) {
return "Choose a color to add a segment.";
}
const candidateFrom = this._getSegmentPreviewBoundaryValue(parsedFrom.value);
const candidateTo = this._getSegmentPreviewBoundaryValue(parsedTo.value);
const rows = this._buildSegmentValidationRows(scope);
for (const row of rows) {
if (row.parsedFrom.state !== "valid" || row.parsedTo.state !== "valid") continue;
const otherFrom = this._getSegmentPreviewBoundaryValue(row.parsedFrom.value);
const otherTo = this._getSegmentPreviewBoundaryValue(row.parsedTo.value);
if (candidateFrom === otherFrom) {
return "Duplicate segment start.";
}
if (candidateFrom < otherTo && candidateTo > otherFrom) {
return "Segments overlap.";
}
}
return "";
}
_getSegmentPreviewBoundaryValue(value) {
if (typeof value === "string") {
const match = value.trim().match(/^([+-]?(?:\d+(?:\.\d+)?|\.\d+))%$/);
if (match) {
const parsed = parseFloat(match[1]);
return Number.isFinite(parsed) ? parsed : null;
}
}
if (typeof value === "number" && Number.isFinite(value)) {
return value;
}
if (this._isObject(value)) {
if (Number.isFinite(value.percent)) {
return value.percent;
}
const fixedValue = this._getFiniteNumber(value.fixed);
if (Number.isFinite(fixedValue)) {
return fixedValue;
}
}
return null;
}
_sortSegmentsForEditor(segments) {
if (!Array.isArray(segments)) {
return [];
}
return this._cloneDeep(segments).sort((left, right) => {
const leftFrom = this._getSegmentPreviewBoundaryValue(left == null ? void 0 : left.from);
const rightFrom = this._getSegmentPreviewBoundaryValue(right == null ? void 0 : right.from);
if (leftFrom === null && rightFrom === null) return 0;
if (leftFrom === null) return 1;
if (rightFrom === null) return -1;
return leftFrom - rightFrom;
});
}
_getSegmentPreviewRows(scope = { type: "card" }) {
var _a;
const baseSegments = (_a = this._getSegmentsUiRows(scope)) != null ? _a : this._getScopedSegmentsValue(scope);
const previewSegments = this._sortSegmentsForEditor(baseSegments);
const validDraft = this._getValidSegmentDraft(scope);
if (validDraft) {
previewSegments.push(validDraft);
}
return this._sortSegmentsForEditor(previewSegments).filter((segment) => {
const from = this._getSegmentPreviewBoundaryValue(segment == null ? void 0 : segment.from);
const to = this._getSegmentPreviewBoundaryValue(segment == null ? void 0 : segment.to);
return from !== null && to !== null && typeof (segment == null ? void 0 : segment.color) === "string" && segment.color.trim();
});
}
_buildEditorSegmentPreviewStyle(scope = { type: "card" }) {
const segments = this._getSegmentPreviewRows(scope);
if (!segments.length) {
return "";
}
const fillStyle = this._getEffectiveFillStyleValue(scope);
const stops = [];
segments.forEach((segment) => {
const from = Math.max(0, Math.min(100, this._getSegmentPreviewBoundaryValue(segment.from)));
const to = Math.max(0, Math.min(100, this._getSegmentPreviewBoundaryValue(segment.to)));
stops.push(`${segment.color} ${from}%`, `${segment.color} ${to}%`);
});
if (fillStyle === "bands") {
return `background:linear-gradient(to right,${stops.join(",")});background-repeat:no-repeat;`;
}
return `background:linear-gradient(to right,${stops.join(",")});background-repeat:no-repeat;`;
}
_getSegmentPreviewDomIds(scope = { type: "card" }) {
if ((scope == null ? void 0 : scope.type) === "entity") {
return {
previewId: `entity-${scope.index}-segment-preview`,
trackId: `entity-${scope.index}-segment-preview-track`
};
}
return {
previewId: "card-segment-preview",
trackId: "card-segment-preview-track"
};
}
_renderSegmentPreview(scope = { type: "card" }) {
var _a;
const { previewId, trackId } = this._getSegmentPreviewDomIds(scope);
const segments = this._getSegmentPreviewRows(scope);
const markers = [];
segments.forEach((segment) => {
const from = this._getSegmentPreviewBoundaryValue(segment.from);
const to = this._getSegmentPreviewBoundaryValue(segment.to);
if (from !== null) markers.push(from);
if (to !== null) markers.push(to);
});
const uniqueMarkers = [...new Set(markers)].sort((left, right) => left - right);
return `
<div id="${previewId}" class="gradient-preview segment-preview">
<div id="${trackId}" class="gradient-preview-track segment-preview-track" style="${this._escapeAttribute((_a = this._buildEditorSegmentPreviewStyle(scope)) != null ? _a : "")}">
${uniqueMarkers.map((marker, index) => `
<span
id="${previewId}-stop-${index}"
class="gradient-preview-stop"
style="left:${this._escapeAttribute(String(marker))}%"
title="${this._escapeAttribute(`${marker}%`)}"
></span>
`).join("")}
</div>
</div>
`;
}
_refreshSegmentPreview(scope = { type: "card" }) {
var _a;
const { previewId, trackId } = this._getSegmentPreviewDomIds(scope);
const track = this._getShadowElementById(trackId);
if (!track) {
return;
}
track.setAttribute("style", (_a = this._buildEditorSegmentPreviewStyle(scope)) != null ? _a : "");
const segments = this._getSegmentPreviewRows(scope);
const markers = [];
segments.forEach((segment) => {
const from = this._getSegmentPreviewBoundaryValue(segment.from);
const to = this._getSegmentPreviewBoundaryValue(segment.to);
if (from !== null) markers.push(from);
if (to !== null) markers.push(to);
});
const uniqueMarkers = [...new Set(markers)].sort((left, right) => left - right);
track.innerHTML = uniqueMarkers.map((marker, index) => `
<span
id="${previewId}-stop-${index}"
class="gradient-preview-stop"
style="left:${this._escapeAttribute(String(marker))}%"
title="${this._escapeAttribute(`${marker}%`)}"
></span>
`).join("");
}
_getSegmentDomIds(scope = { type: "card" }) {
if ((scope == null ? void 0 : scope.type) === "entity") {
return {
hintPrefix: `entity-${scope.index}-segment-row-hint-`,
draftHintId: `entity-${scope.index}-segment-draft-hint`,
addSelector: `button[data-action="add-entity-segment"][data-index="${scope.index}"]`
};
}
return {
hintPrefix: "segment-row-hint-",
draftHintId: "segment-draft-hint",
addSelector: 'button[data-action="add-segment"]'
};
}
_refreshSegmentUi(scope = { type: "card" }) {
var _a, _b;
this._refreshSegmentPreview(scope);
if (!this.shadowRoot) {
return;
}
const { hintPrefix, draftHintId, addSelector } = this._getSegmentDomIds(scope);
const addButton = this.shadowRoot.querySelector(addSelector);
if (addButton) {
addButton.disabled = !this._canAddSegment(scope);
}
const rows = (_a = this._getSegmentsUiRows(scope)) != null ? _a : this._getScopedSegmentsValue(scope);
rows.forEach((_, index) => {
var _a2;
const hint = this._getShadowElementById(`${hintPrefix}${index}`);
const message = this._getSegmentRowValidationMessage(scope, index);
if (hint) {
hint.textContent = message;
(_a2 = hint.setAttribute) == null ? void 0 : _a2.call(hint, "style", message ? "" : "display:none");
}
});
const draftHint = this._getShadowElementById(draftHintId);
if (draftHint) {
const message = this._getSegmentDraftValidationMessage(scope);
draftHint.textContent = message;
(_b = draftHint.setAttribute) == null ? void 0 : _b.call(draftHint, "style", message ? "" : "display:none");
}
}
_commitSegmentDraft(scope = { type: "card" }) {
var _a;
const draftSegment = this._getValidSegmentDraft(scope);
if (!draftSegment) {
this._refreshSegmentUi(scope);
return false;
}
const committedSegments = (_a = this._getSegmentsUiRows(scope)) != null ? _a : this._getScopedSegmentsValue(scope);
const nextSegments = this._sortSegmentsForEditor([...committedSegments, draftSegment]);
const applied = this._setScopedSegments(scope, nextSegments, { rerender: true });
if (applied !== false) {
this._segmentDrafts.set(this._getSegmentsScopeKey(scope), this._createSegmentDraftState(scope));
}
return applied;
}
_commitSegmentBoundaryEdit(scope = { type: "card" }, segmentIndex, field, rawValue, inputEl = null) {
var _a, _b;
this._setSegmentBoundaryText(scope, segmentIndex, field, rawValue);
const normalizedText = this._normalizeTextValue(rawValue).trim();
const parsedValue = this._parseSegmentBoundaryInput(rawValue);
const nextValue = parsedValue === null ? normalizedText : parsedValue;
const currentSegments = (_a = this._getSegmentsUiRows(scope)) != null ? _a : this._getScopedSegmentsValue(scope);
const nextSegments = currentSegments.map((segment, currentIndex) => currentIndex === segmentIndex ? { ...segment, [field]: nextValue } : segment);
this._clearSegmentBoundaryText(scope, segmentIndex, field);
const applied = this._setScopedSegments(scope, nextSegments, { rerender: true });
const message = this._getSegmentRowValidationMessage(scope, segmentIndex);
if (inputEl == null ? void 0 : inputEl.setCustomValidity) {
inputEl.setCustomValidity(message || "");
if (message) {
(_b = inputEl.reportValidity) == null ? void 0 : _b.call(inputEl);
}
}
return applied;
}
_commitGradientStopDraft(scope = { type: "card" }) {
const draftStop = this._getValidGradientDraftStop(scope);
if (!draftStop || this._hasGradientStopDuplicate(scope, draftStop.pos)) {
this._refreshGradientDraftUi(scope);
return false;
}
const committedStops = this._sanitizeGradientStopsForEmit(this._getScopedGradientStopsValue(scope));
const nextStops = [...committedStops, draftStop].sort((left, right) => left.pos - right.pos);
const applied = this._setScopedGradientStops(scope, nextStops, { rerender: true });
if (applied !== false) {
this._gradientStopsDrafts.set(this._getGradientStopsDraftKey(scope), {
pos: (() => {
const suggestion = this._getNextSuggestedGradientStopPos(scope);
return suggestion === "" ? "" : String(suggestion);
})(),
color: draftStop.color
});
}
return applied;
}
_getGradientPreviewDomIds(scope = { type: "card" }) {
if ((scope == null ? void 0 : scope.type) === "entity") {
return {
previewId: `entity-${scope.index}-gradient-preview`,
trackId: `entity-${scope.index}-gradient-preview-track`,
hintId: `entity-${scope.index}-gradient-draft-hint`,
addSelector: `button[data-action="add-entity-gradient-stop"][data-index="${scope.index}"]`,
draftInputId: `entity-${scope.index}-gradient-draft-pos`
};
}
return {
previewId: "card-gradient-preview",
trackId: "card-gradient-preview-track",
hintId: "gradient-draft-hint",
addSelector: 'button[data-action="add-gradient-stop"]',
draftInputId: "gradient-draft-pos"
};
}
_refreshGradientDraftUi(scope = { type: "card" }) {
var _a, _b, _c;
if (!this.shadowRoot) {
return;
}
const getById = (_b = (_a = this.shadowRoot.getElementById) == null ? void 0 : _a.bind(this.shadowRoot)) != null ? _b : ((id) => {
var _a2, _b2, _c2;
return (_c2 = (_b2 = (_a2 = this.shadowRoot).querySelector) == null ? void 0 : _b2.call(_a2, `#${id}`)) != null ? _c2 : null;
});
const { previewId, trackId, hintId, addSelector, draftInputId } = this._getGradientPreviewDomIds(scope);
const addButton = this.shadowRoot.querySelector(addSelector);
if (addButton) {
addButton.disabled = !this._canAddGradientStop(scope);
}
const draftInput = getById(draftInputId);
if (draftInput && typeof draftInput.closest !== "function") {
this._render();
return;
}
const draftContainer = draftInput == null ? void 0 : draftInput.closest(".gradient-stop-draft");
const nextMessage = this._getGradientDraftValidationMessage(scope);
const existingHint = getById(hintId);
if (nextMessage) {
if (existingHint) {
existingHint.textContent = nextMessage;
} else if (draftContainer) {
const hint = document.createElement("div");
hint.id = hintId;
hint.className = "section-note";
hint.textContent = nextMessage;
draftContainer.appendChild(hint);
}
} else if (existingHint) {
existingHint.remove();
}
const preview = getById(previewId);
const track = getById(trackId);
if (!preview || !track) {
return;
}
track.setAttribute("style", (_c = this._getGradientPreviewStyle(scope)) != null ? _c : "");
const markerStops = this._buildGradientPreviewEffectiveStops(scope);
const renderedStops = markerStops.length ? markerStops : this._getDefaultGradientStops();
track.innerHTML = renderedStops.map((stop, index) => `
<span
id="${previewId}-stop-${index}"
class="gradient-preview-stop"
style="left:${this._escapeAttribute(String(stop.pos))}%"
title="${this._escapeAttribute(`${stop.pos}%`)}"
></span>
`).join("");
}
_commitGradientStopPosEdit(scope = { type: "card" }, stopIndex, rawValue, inputEl = null) {
const nextPos = this._normalizeGradientStopPosValue(rawValue);
if (nextPos === null || this._hasGradientStopDuplicate(scope, nextPos, stopIndex)) {
if (inputEl == null ? void 0 : inputEl.setCustomValidity) {
inputEl.setCustomValidity(nextPos === null ? "Enter a value from 0 to 100." : "Position already exists.");
if (inputEl.reportValidity) {
inputEl.reportValidity();
}
}
return false;
}
if (inputEl == null ? void 0 : inputEl.setCustomValidity) {
inputEl.setCustomValidity("");
}
const currentStops = this._getScopedGradientStopsValue(scope);
const nextStops = currentStops.map((stop, currentStopIndex) => currentStopIndex === stopIndex ? { ...stop, pos: nextPos } : stop);
this._clearGradientStopPosText(scope, stopIndex);
return this._setScopedGradientStops(scope, nextStops, { rerender: true });
}
_getFillStyleValue() {
return this._getEffectiveFillStyleValue({ type: "card" });
}
_getFillStyleFromColorMode(colorMode) {
switch (colorMode) {
case "single":
return "solid";
case "gradient":
return "gradient";
case "severity":
return "bands";
case "severity_gradient":
return "band_gradient";
default:
return "bands";
}
}
_getScopedFillStyleValue(scope) {
var _a;
const fillStyle = this._getScopedValue(scope, ["bar", "fill_style"]);
if (fillStyle) return fillStyle;
const colorMode = (_a = this._getScopedValue(scope, ["bar", "color_mode"])) != null ? _a : this._getScopedValue(scope, ["color_mode"]);
return this._getFillStyleFromColorMode(colorMode);
}
_getEffectiveScopedFillStyleValue(scope) {
return this._getEffectiveFillStyleValue(scope);
}
_setScopedBarFillStyle(scope, rawValue) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
if (!normalizedValue) {
return this._removeCanonicalScopedValue(scope, ["bar", "fill_style"], {
deprecatedKeys: [["color_mode"]],
prunePaths: [["bar"]]
});
}
return this._setCanonicalScopedTextOverride(scope, ["bar", "fill_style"], normalizedValue, {
deprecatedKeys: [["color_mode"]],
prunePaths: [["bar"]]
});
}
_getScopedBarColorValue(scope) {
var _a, _b;
return (_b = (_a = this._getScopedValue(scope, ["bar", "color"])) != null ? _a : this._getScopedValue(scope, ["color"])) != null ? _b : "#4a9eff";
}
_getEffectiveScopedBarColorValue(scope) {
const value = this._getEffectiveScopedDisplayValue(scope, ["bar", "color"], [["color"]]);
return value || "#4a9eff";
}
_setScopedBarColor(scope, rawValue) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
if (!normalizedValue || this._normalizeColorComparisonValue(normalizedValue) === this._normalizeColorComparisonValue("#4a9eff")) {
return this._removeCanonicalScopedValue(scope, ["bar", "color"], {
deprecatedKeys: [["color"]],
prunePaths: [["bar"]]
});
}
return this._setCanonicalScopedTextOverride(scope, ["bar", "color"], normalizedValue, {
deprecatedKeys: [["color"]],
prunePaths: [["bar"]]
});
}
_getScopedBarSolidFillValue(scope) {
return !!this._getScopedValue(scope, ["bar", "solid_fill"]);
}
_getEffectiveScopedBarSolidFillValue(scope) {
if ((scope == null ? void 0 : scope.type) !== "entity") {
return this._getScopedBarSolidFillValue(scope);
}
const localValue = this._getScopedValue(scope, ["bar", "solid_fill"]);
if (localValue !== void 0) {
return !!localValue;
}
return this._getScopedBarSolidFillValue({ type: "card" });
}
_setScopedBarSolidFill(scope, value) {
if (!value) {
return this._removeCanonicalScopedValue(scope, ["bar", "solid_fill"], {
prunePaths: [["bar"]]
});
}
return this._setCanonicalScopedValue(scope, ["bar", "solid_fill"], true, {
prunePaths: [["bar"]]
});
}
_clearEntityBarAppearance(scope) {
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._deletePathValue(target, ["bar", "fill_style"]);
nextTarget = this._deletePathValue(nextTarget, ["bar", "color"]);
nextTarget = this._deletePathValue(nextTarget, ["bar", "solid_fill"]);
nextTarget = this._deletePathValue(nextTarget, ["color_mode"]);
nextTarget = this._deletePathValue(nextTarget, ["color"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["bar"]);
return nextTarget;
}, { rerender: true });
}
_hasEntityBarAppearanceOverride(scope) {
var _a;
const barValue = (_a = this._getScopedValue(scope, ["bar"])) != null ? _a : {};
if (this._isObject(barValue) && (Object.prototype.hasOwnProperty.call(barValue, "fill_style") || Object.prototype.hasOwnProperty.call(barValue, "color") || Object.prototype.hasOwnProperty.call(barValue, "solid_fill"))) {
return true;
}
return this._getScopedValue(scope, ["color_mode"]) !== void 0 || this._getScopedValue(scope, ["color"]) !== void 0;
}
_getScopedNeedleConfig(scope) {
var _a;
const rawNeedle = this._getScopedValue(scope, ["bar", "needle"]);
const defaultColor = "#ffffff";
let mode = (scope == null ? void 0 : scope.type) === "entity" ? "inherit" : "disabled";
let color = "";
if (typeof rawNeedle === "boolean") {
mode = rawNeedle ? "enabled" : "disabled";
} else if (this._isObject(rawNeedle)) {
if (rawNeedle.show === true) {
mode = "enabled";
} else if (rawNeedle.show === false) {
mode = "disabled";
} else if ((scope == null ? void 0 : scope.type) !== "entity") {
mode = "disabled";
}
color = (_a = rawNeedle.color) != null ? _a : "";
}
if (color === defaultColor) {
color = "";
}
return { mode, color };
}
_hasNeedleOverride(scope) {
return this._getScopedValue(scope, ["bar", "needle"]) !== void 0;
}
_getEffectiveScopedNeedleConfig(scope) {
const localNeedle = this._getScopedNeedleConfig(scope);
if ((scope == null ? void 0 : scope.type) !== "entity") {
return localNeedle;
}
if (!this._hasNeedleOverride(scope)) {
return this._getScopedNeedleConfig({ type: "card" });
}
const inheritedNeedle = this._getScopedNeedleConfig({ type: "card" });
return {
mode: localNeedle.mode === "inherit" ? inheritedNeedle.mode : localNeedle.mode,
color: localNeedle.color || inheritedNeedle.color
};
}
_setScopedNeedleMode(scope, mode) {
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._cloneDeep(target);
const existingNeedle = this._getPathValue(nextTarget, ["bar", "needle"]);
const existingColor = this._isObject(existingNeedle) ? existingNeedle.color : void 0;
nextTarget = this._deletePathValue(nextTarget, ["bar", "needle"]);
if ((scope == null ? void 0 : scope.type) === "entity" && mode === "inherit") {
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["bar"]);
return nextTarget;
}
if (mode === "disabled") {
if ((scope == null ? void 0 : scope.type) === "entity") {
nextTarget = this._setPathValue(nextTarget, ["bar", "needle"], { show: false });
}
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["bar"]);
return nextTarget;
}
const nextNeedle = { show: true };
if (existingColor && existingColor !== "#ffffff") {
nextNeedle.color = existingColor;
}
nextTarget = this._setPathValue(nextTarget, ["bar", "needle"], nextNeedle);
const baselineEnabled = this._getPathValue(nextTarget, ["baseline", "enabled"]);
const baselineParts = this._getResolvablePartsFromTarget(nextTarget != null ? nextTarget : {}, "baseline", {
canonicalBasePath: ["baseline", "at"],
legacyFixedPath: ["baseline"],
legacyEntityPath: ["baseline", "at", "entity"]
});
const baselineActive = baselineEnabled === true || baselineEnabled !== false && this._hasResolvableOverride(baselineParts);
if (baselineActive) {
nextTarget = this._deletePathValue(nextTarget, ["baseline"]);
}
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["bar"]);
return nextTarget;
});
}
_setScopedNeedleColor(scope, rawValue) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._cloneDeep(target);
const current = this._getScopedNeedleConfig(scope);
const hasCustomColor = normalizedValue && this._normalizeColorComparisonValue(normalizedValue) !== this._normalizeColorComparisonValue("#ffffff");
nextTarget = this._deletePathValue(nextTarget, ["bar", "needle", "color"]);
if ((scope == null ? void 0 : scope.type) !== "entity" && current.mode === "disabled") {
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["bar", "needle"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["bar"]);
return nextTarget;
}
const showValue = current.mode === "enabled" ? true : current.mode === "disabled" ? false : void 0;
const nextNeedle = {};
if (showValue !== void 0) {
nextNeedle.show = showValue;
}
if (hasCustomColor) {
nextNeedle.color = normalizedValue;
}
if (Object.keys(nextNeedle).length) {
nextTarget = this._setPathValue(nextTarget, ["bar", "needle"], nextNeedle);
} else {
nextTarget = this._deletePathValue(nextTarget, ["bar", "needle"]);
}
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["bar", "needle"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["bar"]);
return nextTarget;
});
}
_getNeedleValue() {
return this._getScopedNeedleConfig({ type: "card" }).mode === "enabled";
}
_getPeakShowValue() {
return this._getScopedPeakConfig({ type: "card" }).mode === "enabled";
}
_getScaleFixedValue(key, fallbackKey) {
return this._getResolvableScopedValue({ type: "card" }, key).fixed;
}
_getScaleEntityValue(key) {
return this._getResolvableScopedValue({ type: "card" }, key).entity;
}
_getTargetResolvableValue(scope) {
return this._getResolvableScopedValue(scope, "target", {
canonicalBasePath: ["target", "at"],
legacyFixedPath: ["target"],
legacyEntityPath: ["target_entity"]
});
}
_getEffectiveTargetResolvableValue(scope) {
return this._getEffectiveResolvableScopedValue(scope, "target", {
canonicalBasePath: ["target", "at"],
legacyFixedPath: ["target"],
legacyEntityPath: ["target_entity"]
});
}
_getTargetMode(scope) {
const enabled = this._getScopedValue(scope, ["target", "enabled"]);
if ((scope == null ? void 0 : scope.type) === "entity") {
if (enabled === false) return "disabled";
if (enabled === true || this._hasTargetOverride(scope)) return "enabled";
return "inherit";
}
if (enabled === true) return "enabled";
if (enabled === false) return "disabled";
return "auto";
}
_getTargetShapeValue(scope) {
var _a;
return (_a = this._getScopedValue(scope, ["target", "shape"])) != null ? _a : "";
}
_getEffectiveMarkerDirection(scope, key) {
const canonical = this._getScopedValue(scope, [key, "direction"]);
const local = canonical !== void 0 ? canonical : this._getScopedValue(scope, [`${key}_marker`, "direction"]);
if ((scope == null ? void 0 : scope.type) === "entity" && local === void 0) {
return this._getEffectiveMarkerDirection({ type: "card" }, key);
}
return normalizeMarkerDirection(local);
}
_setMarkerDirection(scope, key, rawValue) {
const direction = normalizeMarkerDirection(rawValue);
const cardDirection = this._getEffectiveMarkerDirection({ type: "card" }, key);
if ((scope == null ? void 0 : scope.type) !== "entity" && direction === "inward" || (scope == null ? void 0 : scope.type) === "entity" && direction === cardDirection) {
return this._removeCanonicalScopedValue(scope, [key, "direction"], {
prunePaths: [[key]]
});
}
return this._setCanonicalScopedTextOverride(scope, [key, "direction"], direction, {
prunePaths: [[key]]
});
}
_hasTargetShape(scope) {
const target = this._getScopedValue(scope, ["target"]);
return this._isObject(target) && Object.prototype.hasOwnProperty.call(target, "shape");
}
_getEffectiveTargetShapeValue(scope) {
if ((scope == null ? void 0 : scope.type) === "entity" && !this._hasTargetShape(scope)) {
return this._getEffectiveTargetShapeValue({ type: "card" });
}
return normalizeTargetMarkerShape(this._getTargetShapeValue(scope));
}
_setTargetShape(scope, rawValue) {
const normalizedShape = normalizeTargetMarkerShape(rawValue);
if ((scope == null ? void 0 : scope.type) !== "entity" && normalizedShape === "diamond") {
return this._removeCanonicalScopedValue(scope, ["target", "shape"], {
prunePaths: [["target"]]
});
}
return this._setCanonicalScopedTextOverride(scope, ["target", "shape"], normalizedShape, {
prunePaths: [["target"]]
});
}
_getEffectiveTargetMode(scope) {
const mode = this._getTargetMode(scope);
if ((scope == null ? void 0 : scope.type) !== "entity" || mode !== "inherit") {
return mode;
}
const cardMode = this._getTargetMode({ type: "card" });
if (cardMode === "disabled") return "disabled";
if (cardMode === "enabled") return "enabled";
const cardTarget = this._getTargetResolvableValue({ type: "card" });
return this._hasResolvableOverride(cardTarget) || this._hasCustomTargetColor({ type: "card" }) || this._getTargetLabelShowValue({ type: "card" }) || !!this._getTargetAboveFillColorValue({ type: "card" }) ? "enabled" : "disabled";
}
_setTargetMode(scope, mode) {
if ((scope == null ? void 0 : scope.type) === "entity" && mode === "inherit") {
return this._clearTargetOverride(scope);
}
if (mode === "auto") {
return this._removeCanonicalScopedValue(scope, ["target", "enabled"], {
prunePaths: [["target"]]
});
}
return this._setCanonicalScopedValue(scope, ["target", "enabled"], mode === "enabled", {
prunePaths: [["target"]]
});
}
_setTargetResolvablePart(scope, part, rawValue) {
return this._setCanonicalResolvablePart(scope, "target", part, rawValue, {
canonicalBasePath: ["target", "at"],
legacyFixedPath: ["target"],
legacyEntityPath: ["target_entity"],
prunePaths: [["target", "at"], ["target"]]
});
}
_clearTargetOverride(scope) {
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._cloneDeep(target);
const rawTarget = this._getPathValue(nextTarget, ["target"]);
if (this._isObject(rawTarget)) {
nextTarget = this._deletePathValue(nextTarget, ["target", "enabled"]);
nextTarget = this._deletePathValue(nextTarget, ["target", "at"]);
nextTarget = this._deletePathValue(nextTarget, ["target", "color"]);
nextTarget = this._deletePathValue(nextTarget, ["target", "shape"]);
nextTarget = this._deletePathValue(nextTarget, ["target", "direction"]);
nextTarget = this._deletePathValue(nextTarget, ["target", "label", "show"]);
nextTarget = this._deletePathValue(nextTarget, ["target", "label", "decimal"]);
nextTarget = this._deletePathValue(nextTarget, ["target", "when_exceeded", "fill_color"]);
} else {
nextTarget = this._deletePathValue(nextTarget, ["target"]);
}
nextTarget = this._deletePathValue(nextTarget, ["target_entity"]);
nextTarget = this._deletePathValue(nextTarget, ["target_color"]);
nextTarget = this._deletePathValue(nextTarget, ["show_target_label"]);
nextTarget = this._deletePathValue(nextTarget, ["above_target_color"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["target", "label"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["target", "when_exceeded"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["target"]);
return nextTarget;
}, { rerender: true });
}
_getTargetColorValue(scope) {
var _a, _b;
return (_b = (_a = this._getScopedValue(scope, ["target", "color"])) != null ? _a : this._getScopedValue(scope, ["target_color"])) != null ? _b : "";
}
_getEffectiveTargetColorValue(scope) {
return this._getEffectiveScopedDisplayValue(scope, ["target", "color"], [["target_color"]]);
}
_hasCustomTargetColor(scope) {
const color = this._getTargetColorValue(scope);
return !!color && this._normalizeColorComparisonValue(color) !== this._normalizeColorComparisonValue("#888");
}
_setTargetColor(scope, rawValue) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
if (!normalizedValue || this._normalizeColorComparisonValue(normalizedValue) === this._normalizeColorComparisonValue("#888")) {
return this._removeCanonicalScopedValue(scope, ["target", "color"], {
deprecatedKeys: [["target_color"]],
prunePaths: [["target"]]
});
}
return this._setCanonicalScopedTextOverride(scope, ["target", "color"], normalizedValue, {
deprecatedKeys: [["target_color"]],
prunePaths: [["target"]]
});
}
_getTargetLabelShowValue(scope) {
const structuredValue = this._getScopedValue(scope, ["target", "label", "show"]);
if (structuredValue !== void 0) {
return !!structuredValue;
}
return !!this._getScopedValue(scope, ["show_target_label"]);
}
_getEffectiveTargetLabelShowValue(scope) {
const structuredValue = this._getScopedValue(scope, ["target", "label", "show"]);
if (structuredValue !== void 0) {
return !!structuredValue;
}
const legacyValue = this._getScopedValue(scope, ["show_target_label"]);
if (legacyValue !== void 0) {
return !!legacyValue;
}
if ((scope == null ? void 0 : scope.type) === "entity") {
return this._getTargetLabelShowValue({ type: "card" });
}
return false;
}
_setTargetLabelShow(scope, value) {
if (!value) {
return this._removeCanonicalScopedValue(scope, ["target", "label", "show"], {
deprecatedKeys: [["show_target_label"]],
prunePaths: [["target", "label"], ["target"]]
});
}
return this._setCanonicalScopedValue(scope, ["target", "label", "show"], true, {
deprecatedKeys: [["show_target_label"]],
prunePaths: [["target", "label"], ["target"]]
});
}
_getTargetLabelDecimalValue(scope) {
var _a, _b;
return (_b = (_a = this._getScopedValue(scope, ["target", "label", "precision"])) != null ? _a : this._getScopedValue(scope, ["target", "label", "decimal"])) != null ? _b : "";
}
_getEffectiveTargetLabelDecimalValue(scope) {
const local = this._getTargetLabelDecimalValue(scope);
return local !== "" && local !== null && local !== void 0 ? local : (scope == null ? void 0 : scope.type) === "entity" ? this._getTargetLabelDecimalValue({ type: "card" }) : "";
}
_setTargetLabelDecimal(scope, rawValue) {
const normalizedValue = this._normalizeDecimalValue(rawValue);
if (rawValue === "" || rawValue === null || rawValue === void 0) {
return this._removeCanonicalScopedValue(scope, ["target", "label", "decimal"], {
prunePaths: [["target", "label"], ["target"]]
});
}
if (normalizedValue === null) {
return false;
}
return this._setCanonicalScopedValue(scope, ["target", "label", "decimal"], normalizedValue, {
prunePaths: [["target", "label"], ["target"]]
});
}
_getTargetAboveFillColorValue(scope) {
var _a, _b;
return (_b = (_a = this._getScopedValue(scope, ["target", "when_exceeded", "fill_color"])) != null ? _a : this._getScopedValue(scope, ["above_target_color"])) != null ? _b : "";
}
_getTargetAboveFillDraftKey(scope = { type: "card" }) {
return (scope == null ? void 0 : scope.type) === "entity" ? `entity:${scope.index}` : "card";
}
_setTargetAboveFillDraft(scope, rawValue) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
const key = this._getTargetAboveFillDraftKey(scope);
if (normalizedValue) {
this._targetAboveFillDrafts.set(key, normalizedValue);
} else {
this._targetAboveFillDrafts.delete(key);
}
}
_getTargetAboveFillDraft(scope) {
var _a;
return (_a = this._targetAboveFillDrafts.get(this._getTargetAboveFillDraftKey(scope))) != null ? _a : "";
}
_getBaselineColorDraftKey(scope = { type: "card" }, direction = "above") {
const scopeKey = (scope == null ? void 0 : scope.type) === "entity" ? `entity:${scope.index}` : "card";
return `${scopeKey}:${direction}`;
}
_setBaselineColorDraft(scope, direction, rawValue) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
const key = this._getBaselineColorDraftKey(scope, direction);
if (normalizedValue) {
this._baselineColorDrafts.set(key, normalizedValue);
} else {
this._baselineColorDrafts.delete(key);
}
}
_getBaselineColorDraft(scope, direction) {
var _a;
return (_a = this._baselineColorDrafts.get(this._getBaselineColorDraftKey(scope, direction))) != null ? _a : "";
}
_getEffectiveTargetAboveFillColorValue(scope) {
return this._getEffectiveScopedDisplayValue(scope, ["target", "when_exceeded", "fill_color"], [["above_target_color"]]);
}
_setTargetAboveFillColor(scope, rawValue) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
this._setTargetAboveFillDraft(scope, normalizedValue);
if (!this._isTargetAboveFillEnabled(scope)) {
return false;
}
if (!normalizedValue) {
return this._removeCanonicalScopedValue(scope, ["target", "when_exceeded", "fill_color"], {
deprecatedKeys: [["above_target_color"]],
prunePaths: [["target", "when_exceeded"], ["target"]]
});
}
return this._setCanonicalScopedTextOverride(scope, ["target", "when_exceeded", "fill_color"], normalizedValue, {
deprecatedKeys: [["above_target_color"]],
prunePaths: [["target", "when_exceeded"], ["target"]]
});
}
_isTargetAboveFillEnabled(scope) {
return !!this._normalizeTextValue(this._getTargetAboveFillColorValue(scope)).trim();
}
_setTargetAboveFillEnabled(scope, value) {
const currentValue = this._normalizeTextValue(this._getTargetAboveFillColorValue(scope)).trim();
if (!value) {
if (currentValue) {
this._setTargetAboveFillDraft(scope, currentValue);
}
return this._removeCanonicalScopedValue(scope, ["target", "when_exceeded", "fill_color"], {
deprecatedKeys: [["above_target_color"]],
prunePaths: [["target", "when_exceeded"], ["target"]]
});
}
const nextValue = this._getTargetAboveFillDraft(scope) || this._normalizeTextValue(this._getEffectiveTargetAboveFillColorValue(scope)).trim() || currentValue || "#000000";
return this._setCanonicalScopedTextOverride(scope, ["target", "when_exceeded", "fill_color"], nextValue, {
deprecatedKeys: [["above_target_color"]],
prunePaths: [["target", "when_exceeded"], ["target"]]
});
}
_isBaselineDirectionalColorEnabled(scope, direction) {
return !!this._normalizeTextValue(this._getBaselineDirectionalColorValue(scope, direction)).trim();
}
_setBaselineDirectionalColorEnabled(scope, direction, value) {
const currentValue = this._normalizeTextValue(this._getBaselineDirectionalColorValue(scope, direction)).trim();
if (!value) {
if (currentValue) {
this._setBaselineColorDraft(scope, direction, currentValue);
}
return this._removeCanonicalScopedValue(scope, ["baseline", direction, "color"], {
prunePaths: [["baseline", direction], ["baseline"]]
});
}
const nextValue = this._getBaselineColorDraft(scope, direction) || this._normalizeTextValue(this._getEffectiveBaselineDirectionalColorValue(scope, direction)).trim() || currentValue || "#000000";
return this._setCanonicalScopedTextOverride(scope, ["baseline", direction, "color"], nextValue, {
prunePaths: [["baseline", direction], ["baseline"]]
});
}
_hasTargetOverride(scope) {
const targetValue = this._getScopedValue(scope, ["target"]);
if (this._isObject(targetValue) && Object.keys(targetValue).length) {
return true;
}
if (!this._isObject(targetValue) && targetValue !== void 0 && targetValue !== null && targetValue !== "") {
return true;
}
return ["target_entity", "target_color", "show_target_label", "above_target_color"].some((key) => {
const value = this._getScopedValue(scope, [key]);
return value !== void 0 && value !== null && value !== "" && value !== false;
}) || this._getTargetLabelDecimalValue(scope) !== "";
}
_getBaselineResolvableValue(scope) {
return this._getResolvableScopedValue(scope, "baseline", {
canonicalBasePath: ["baseline", "at"],
legacyFixedPath: ["baseline"],
legacyEntityPath: ["baseline", "at", "entity"]
});
}
_getEffectiveBaselineResolvableValue(scope) {
return this._getEffectiveResolvableScopedValue(scope, "baseline", {
canonicalBasePath: ["baseline", "at"],
legacyFixedPath: ["baseline"],
legacyEntityPath: ["baseline", "at", "entity"]
});
}
_getBaselineMode(scope) {
const enabled = this._getScopedValue(scope, ["baseline", "enabled"]);
if ((scope == null ? void 0 : scope.type) === "entity") {
if (enabled === false) return "disabled";
if (enabled === true || this._hasBaselineOverride(scope)) return "enabled";
return "inherit";
}
if (enabled === true) return "enabled";
if (enabled === false) return "disabled";
return "auto";
}
_getEffectiveBaselineMode(scope) {
const mode = this._getBaselineMode(scope);
if ((scope == null ? void 0 : scope.type) !== "entity" || mode !== "inherit") {
return mode;
}
const cardMode = this._getBaselineMode({ type: "card" });
if (cardMode === "disabled") return "disabled";
if (cardMode === "enabled") return "enabled";
const cardBaseline = this._getBaselineResolvableValue({ type: "card" });
return this._hasResolvableOverride(cardBaseline) || !!this._getBaselineDirectionalColorValue({ type: "card" }, "above") || !!this._getBaselineDirectionalColorValue({ type: "card" }, "below") ? "enabled" : "disabled";
}
_setBaselineMode(scope, mode) {
if ((scope == null ? void 0 : scope.type) === "entity" && mode === "inherit") {
return this._clearBaselineOverride(scope);
}
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._cloneDeep(target);
if (mode === "auto") {
nextTarget = this._deletePathValue(nextTarget, ["baseline", "enabled"]);
} else {
nextTarget = this._setPathValue(nextTarget, ["baseline", "enabled"], mode === "enabled");
}
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["baseline"]);
return nextTarget;
});
}
_setBaselineResolvablePart(scope, part, rawValue) {
const normalizedValue = part === "fixed" ? this._normalizeNumberValue(rawValue) : this._normalizeTextValue(rawValue).trim();
return this._applyScopedMutation(scope, (target) => {
const currentParts = this._getResolvablePartsFromTarget(target != null ? target : {}, "baseline", {
canonicalBasePath: ["baseline", "at"],
legacyFixedPath: ["baseline"],
legacyEntityPath: ["baseline", "at", "entity"]
});
const nextParts = { ...currentParts };
if (part === "fixed") {
if (rawValue === "" || rawValue === null || rawValue === void 0 || normalizedValue === null) {
delete nextParts.fixed;
} else {
nextParts.fixed = normalizedValue;
}
} else if (!normalizedValue) {
delete nextParts.entity;
} else {
nextParts.entity = normalizedValue;
}
let nextTarget = this._cloneDeep(target);
nextTarget = this._deletePathValue(nextTarget, ["baseline", "at"]);
const hasFixed = nextParts.fixed !== void 0 && nextParts.fixed !== null && nextParts.fixed !== "";
const hasEntity = nextParts.entity !== void 0 && nextParts.entity !== null && nextParts.entity !== "";
if (hasFixed || hasEntity) {
const nextValue = {};
if (hasFixed) nextValue.fixed = nextParts.fixed;
if (hasEntity) nextValue.entity = nextParts.entity;
nextTarget = this._setPathValue(nextTarget, ["baseline", "at"], nextValue);
}
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["baseline", "at"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["baseline"]);
return nextTarget;
});
}
_removeScopedNeedle(scope) {
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._deletePathValue(target, ["bar", "needle"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["bar"]);
return nextTarget;
});
}
_setBaselineDirectionalColor(scope, direction, rawValue) {
const normalizedValue = this._normalizeTextValue(rawValue).trim();
this._setBaselineColorDraft(scope, direction, normalizedValue);
const path = ["baseline", direction, "color"];
if (!normalizedValue) {
return this._removeCanonicalScopedValue(scope, path, {
prunePaths: [["baseline", direction], ["baseline"]]
});
}
if (!this._isBaselineDirectionalColorEnabled(scope, direction)) {
return this._setBaselineDirectionalColorEnabled(scope, direction, true);
}
return this._applyScopedMutation(scope, (target) => {
return this._setPathValue(target, path, normalizedValue);
});
}
_getBaselineDirectionalColorValue(scope, direction) {
var _a;
return (_a = this._getScopedValue(scope, ["baseline", direction, "color"])) != null ? _a : "";
}
_getEffectiveBaselineDirectionalColorValue(scope, direction) {
return this._getEffectiveScopedDisplayValue(scope, ["baseline", direction, "color"]);
}
_clearBaselineOverride(scope) {
return this._applyScopedMutation(scope, (target) => {
let nextTarget = this._cloneDeep(target);
const rawBaseline = this._getPathValue(nextTarget, ["baseline"]);
if (this._isObject(rawBaseline)) {
nextTarget = this._deletePathValue(nextTarget, ["baseline", "enabled"]);
nextTarget = this._deletePathValue(nextTarget, ["baseline", "at"]);
nextTarget = this._deletePathValue(nextTarget, ["baseline", "above", "color"]);
nextTarget = this._deletePathValue(nextTarget, ["baseline", "below", "color"]);
} else {
nextTarget = this._deletePathValue(nextTarget, ["baseline"]);
}
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["baseline", "above"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["baseline", "below"]);
nextTarget = this._pruneEmptyObjectsInTarget(nextTarget, ["baseline"]);
return nextTarget;
}, { rerender: true });
}
_removeBaseline(scope) {
return this._removeScopedValue(scope, ["baseline"], { rerender: true });
}
_hasBaselineOverride(scope) {
const baselineValue = this._getScopedValue(scope, ["baseline"]);
if (this._isObject(baselineValue) && Object.keys(baselineValue).length) {
return true;
}
return !this._isObject(baselineValue) && baselineValue !== void 0 && baselineValue !== null && baselineValue !== "";
}
_isEntityOverrideExpanded(index) {
return this._expandedEntityOverrides.has(index);
}
_toggleEntityOverrideExpanded(index) {
if (this._expandedEntityOverrides.has(index)) {
this._expandedEntityOverrides.delete(index);
} else {
this._expandedEntityOverrides.add(index);
}
this._render();
}
_syncExpandedEntityOverrides(entityCount) {
const nextExpanded = /* @__PURE__ */ new Set();
this._expandedEntityOverrides.forEach((index) => {
if (index < entityCount) nextExpanded.add(index);
});
this._expandedEntityOverrides = nextExpanded;
const nextGroups = /* @__PURE__ */ new Set();
this._expandedOverrideGroups.forEach((key) => {
const [indexText, group] = String(key).split(":");
const index = Number(indexText);
if (Number.isInteger(index) && index < entityCount && group) {
nextGroups.add(`${index}:${group}`);
}
});
this._expandedOverrideGroups = nextGroups;
}
_isCardGroupExpanded(group) {
return this._expandedCardGroups.has(group);
}
_toggleCardGroupExpanded(group) {
if (this._expandedCardGroups.has(group)) {
this._expandedCardGroups.delete(group);
} else {
this._expandedCardGroups.add(group);
}
this._render();
}
_toggleGenericMarkerExpanded(markerUiId) {
if (this._expandedGenericMarkerUiIds.has(markerUiId)) {
this._expandedGenericMarkerUiIds.delete(markerUiId);
} else {
this._expandedGenericMarkerUiIds.add(markerUiId);
}
this._render();
}
_getOverrideGroupKey(index, group) {
return `${index}:${group}`;
}
_isOverrideGroupExpanded(index, group) {
return this._expandedOverrideGroups.has(this._getOverrideGroupKey(index, group));
}
_toggleOverrideGroupExpanded(index, group) {
const key = this._getOverrideGroupKey(index, group);
if (this._expandedOverrideGroups.has(key)) {
this._expandedOverrideGroups.delete(key);
} else {
this._expandedOverrideGroups.add(key);
}
this._render();
}
_hasExplicitOverrideValue(value) {
return value !== "" && value !== void 0 && value !== null;
}
_hasResolvableOverride(parts) {
return this._hasExplicitOverrideValue(parts == null ? void 0 : parts.fixed) || this._hasExplicitOverrideValue(parts == null ? void 0 : parts.entity);
}
_getScaleOverrideSummary(scope) {
const parts = [];
const min = this._getResolvableScopedValue(scope, "min");
const max = this._getResolvableScopedValue(scope, "max");
if (min.entity) parts.push("Min entity");
else if (min.fixed !== "" && min.fixed !== void 0) parts.push(`Min ${min.fixed}`);
if (max.entity) parts.push("Max entity");
else if (max.fixed !== "" && max.fixed !== void 0) parts.push(`Max ${max.fixed}`);
return parts.length ? parts.join(" \u2022 ") : "Inherited";
}
_getLayoutSummary(scope) {
const parts = [];
const height = this._getScopedLayoutValue(scope, "height");
const position = this._getScopedLayoutValue(scope, "position");
const width = this._getScopedLayoutValue(scope, "width");
if (height !== "") parts.push(`Height ${height}`);
if (position !== "") parts.push(`${position}`);
if (width !== "") parts.push(`Width ${width}`);
return parts.length ? parts.join(" \u2022 ") : "Inherited";
}
_getTargetOverrideSummary(scope) {
const mode = this._getTargetMode(scope);
if (mode === "disabled") return "Disabled";
const parts = [];
const target = this._getTargetResolvableValue(scope);
if (target.fixed !== "" && target.fixed !== void 0) parts.push(`Target ${target.fixed}`);
if (target.entity) parts.push("Entity");
if (this._hasTargetShape(scope)) {
parts.push(this._getEffectiveTargetShapeValue(scope) === "triangle" ? "Triangle" : "Diamond");
}
if (this._hasCustomTargetColor(scope)) parts.push("Custom color");
if (this._getTargetLabelShowValue(scope)) parts.push("Label");
const labelDecimal = this._getTargetLabelDecimalValue(scope);
if (labelDecimal !== "") parts.push(`Label ${labelDecimal} ${Number(labelDecimal) === 1 ? "decimal" : "decimals"}`);
if (this._getTargetAboveFillColorValue(scope)) parts.push("Above");
return parts.length ? parts.join(" \u2022 ") : "Inherited";
}
_getBaselineOverrideSummary(scope) {
const mode = this._getBaselineMode(scope);
if (mode === "disabled") return "Disabled";
const parts = [];
const baseline = this._getBaselineResolvableValue(scope);
if (baseline.fixed !== "" && baseline.fixed !== void 0) parts.push(`Baseline ${baseline.fixed}`);
if (baseline.entity) parts.push("Entity");
if (this._getBaselineDirectionalColorValue(scope, "above")) parts.push("Above");
if (this._getBaselineDirectionalColorValue(scope, "below")) parts.push("Below");
return parts.length ? parts.join(" \u2022 ") : "Inherited";
}
_getCardBaselineSummary() {
const mode = this._getBaselineMode({ type: "card" });
if (mode === "disabled") return "Disabled";
const baseline = this._getBaselineResolvableValue({ type: "card" });
const origin = baseline.entity || (baseline.fixed !== "" && baseline.fixed !== void 0 ? baseline.fixed : "");
const modeLabel = mode === "enabled" ? "Enabled" : "Auto";
return origin !== "" ? `${modeLabel} \xB7 ${origin}` : modeLabel;
}
_getBarAppearanceSummary(scope) {
var _a;
const parts = [];
const fillStyle = this._getScopedFillStyleValue(scope);
const color = (_a = this._getScopedValue(scope, ["bar", "color"])) != null ? _a : this._getScopedValue(scope, ["color"]);
if (fillStyle && fillStyle !== "bands") parts.push(fillStyle.replace(/_/g, " "));
if (color && this._normalizeColorComparisonValue(color) !== this._normalizeColorComparisonValue("#4a9eff")) {
parts.push("Custom color");
}
return parts.length ? parts.join(" \u2022 ") : "Inherited";
}
_getScopedSegmentsValue(scope) {
const uiRows = this._getSegmentsUiRows(scope);
if (uiRows !== null) {
return this._cloneDeep(uiRows);
}
const storedSegments = this._getStoredScopedSegments(scope);
if (storedSegments !== null) {
return this._sortSegmentsForEditor(storedSegments);
}
return this._sortSegmentsForEditor(this._getFallbackSegments(scope));
}
_hasSegmentsOverride(scope) {
return this._getStoredScopedSegments(scope) !== null;
}
_getSegmentsSummary(scope) {
const segments = this._getScopedSegmentsValue(scope);
if (!Array.isArray(segments) || segments.length === 0) {
return "Inherited";
}
if ((scope == null ? void 0 : scope.type) !== "entity" && !this._hasSegmentsOverride(scope) && this._isSegmentFillStyle(this._getEffectiveFillStyleValue(scope))) {
return "Default bands";
}
return `${segments.length} segments`;
}
_getEffectiveFillStyleValue(scope) {
if ((scope == null ? void 0 : scope.type) === "entity") {
return normalizeBarConfig(
this._getEntityRawEntries()[scope.index],
this._draftConfig,
{ isCardScope: false }
).fill_style;
}
return normalizeBarConfig(this._draftConfig, null, { isCardScope: true }).fill_style;
}
_getScopedGradientStopsValue(scope) {
const localRows = this._getGradientStopsUiRows(scope);
if (Array.isArray(localRows)) {
return localRows;
}
const storedStops = this._getStoredScopedGradientStops(scope);
if (storedStops !== null) {
return this._sanitizeGradientStopsForEmit(storedStops);
}
return this._cloneDeep(this._getFallbackGradientStops(scope));
}
_hasGradientStopsOverride(scope) {
if (this._getStoredScopedGradientStops(scope) !== null) {
return true;
}
const localRows = this._getGradientStopsUiRows(scope);
if (!Array.isArray(localRows)) {
return false;
}
return this._serializeConfig(this._sanitizeGradientStopsForEmit(localRows)) !== this._serializeConfig(this._sanitizeGradientStopsForEmit(this._getFallbackGradientStops(scope)));
}
_getGradientStopsSummary(scope) {
if ((scope == null ? void 0 : scope.type) === "entity" && !this._hasGradientStopsOverride(scope)) {
return "Inherited";
}
const gradientStops = this._sanitizeGradientStopsForEmit(this._getScopedGradientStopsValue(scope));
if (this._getEffectiveFillStyleValue(scope) !== "gradient") {
return "Inactive fill style";
}
if (!gradientStops.length) {
return (scope == null ? void 0 : scope.type) === "entity" ? "Inherited" : "Default gradient";
}
if (this._isDefaultGradientStops(gradientStops)) {
return "Default gradient";
}
return `${gradientStops.length} stops`;
}
_buildGradientPreviewEffectiveStops(scope = { type: "card" }) {
const committedStops = this._sanitizeGradientStopsForEmit(this._getScopedGradientStopsValue(scope));
const draftStop = this._getValidGradientDraftStop(scope);
const previewStops = [...committedStops];
if (draftStop && !this._hasGradientStopDuplicate(scope, draftStop.pos)) {
previewStops.push(draftStop);
}
return previewStops.sort((left, right) => left.pos - right.pos);
}
_buildEditorGradientPreviewStyle(stops) {
if (!Array.isArray(stops) || !stops.length) {
return "";
}
const cssStops = stops.map((stop) => {
var _a;
const color = this._normalizeTextValue(stop.color).trim();
const pos = this._normalizeGradientStopPosValue((_a = stop.p) != null ? _a : stop.pos);
if (!color || pos === null) {
return null;
}
return `${color} ${pos}%`;
}).filter(Boolean);
if (!cssStops.length) {
return "";
}
return `background:linear-gradient(to right,${cssStops.join(",")});background-repeat:no-repeat;`;
}
_getGradientPreviewStyle(scope = { type: "card" }) {
const previewStops = this._buildGradientPreviewEffectiveStops(scope);
const resolvedStops = previewStops.length >= 2 ? previewStops.map((stop) => ({ p: stop.pos, color: stop.color })) : this._getDefaultGradientStops().map((stop) => ({ p: stop.pos, color: stop.color }));
return this._buildEditorGradientPreviewStyle(resolvedStops);
}
_renderGradientPreview(scope = { type: "card" }, options = {}) {
var _a, _b, _c;
const previewId = (_a = options.previewId) != null ? _a : "gradient-preview";
const trackId = (_b = options.trackId) != null ? _b : `${previewId}-track`;
const effectiveStops = this._buildGradientPreviewEffectiveStops(scope);
const markerStops = effectiveStops.length ? effectiveStops : this._getDefaultGradientStops();
return `
<div id="${previewId}" class="gradient-preview">
<div id="${trackId}" class="gradient-preview-track" style="${this._escapeAttribute((_c = this._getGradientPreviewStyle(scope)) != null ? _c : "")}">
${markerStops.map((stop, index) => `
<span
id="${previewId}-stop-${index}"
class="gradient-preview-stop"
style="left:${this._escapeAttribute(String(stop.pos))}%"
title="${this._escapeAttribute(`${stop.pos}%`)}"
></span>
`).join("")}
</div>
</div>
`;
}
_getNeedleSummary(scope) {
if ((scope == null ? void 0 : scope.type) === "entity" && !this._hasNeedleOverride(scope)) return "Inherited";
const needle = this._getScopedNeedleConfig(scope);
if (needle.mode === "disabled") return needle.color ? "Disabled \u2022 Custom color" : "Disabled";
if (needle.mode === "enabled") return needle.color ? "Enabled \u2022 Custom color" : "Enabled";
if (needle.color) return "Custom color";
return "Inherited";
}
_getFormattingSummary(scope) {
const parts = [];
const unit = this._getScopedFormattingValue(scope, "unit");
const decimal = this._getScopedFormattingValue(scope, "decimal");
if (unit !== "") parts.push(`Unit ${unit}`);
if (decimal !== "") parts.push(`${decimal} ${Number(decimal) === 1 ? "decimal" : "decimals"}`);
return parts.length ? parts.join(" \u2022 ") : "Inherited";
}
_renderOverrideGroup({ index, group, title, summary, content }) {
const expanded = this._isOverrideGroupExpanded(index, group);
return `
<div class="override-group" data-group="${group}" data-expanded="${expanded ? "true" : "false"}">
<button
type="button"
id="entity-${index}-group-${group}"
class="override-group-toggle"
data-action="toggle-override-group"
data-index="${index}"
data-group="${group}"
aria-expanded="${expanded ? "true" : "false"}"
>
<span
id="entity-${index}-group-${group}-title"
class="override-group-title"
data-action="toggle-override-group"
data-index="${index}"
data-group="${group}"
>${expanded ? "\u25BE" : "\u25B8"} ${title}</span>
<span
id="entity-${index}-group-${group}-summary"
class="override-group-summary"
data-action="toggle-override-group"
data-index="${index}"
data-group="${group}"
>${this._escapeAttribute(summary)}</span>
</button>
<div class="override-group-body" style="display:${expanded ? "grid" : "none"};">
${content}
</div>
</div>
`;
}
_renderCardGroup({ group, title, summary, content, inactive = false }) {
const expanded = this._isCardGroupExpanded(group);
return `
<div class="override-group card-subgroup${inactive ? " is-inactive" : ""}" data-group="${group}" data-expanded="${expanded ? "true" : "false"}">
<button
type="button"
id="card-group-${group}"
class="override-group-toggle"
data-action="toggle-card-group"
data-group="${group}"
aria-expanded="${expanded ? "true" : "false"}"
>
<span id="card-group-${group}-title" class="override-group-title">${expanded ? "\u25BE" : "\u25B8"} ${title}</span>
<span id="card-group-${group}-summary" class="override-group-summary">${this._escapeAttribute(summary)}</span>
</button>
<div class="override-group-body" style="display:${expanded ? "grid" : "none"};">
${content}
</div>
</div>
`;
}
_renderEntityInput(entry, index) {
if (customElements.get("ha-entity-picker")) {
return `<ha-entity-picker data-kind="entity-picker" data-index="${index}"></ha-entity-picker>`;
}
return `<input type="text" data-kind="entity-input" data-index="${index}" value="${this._escapeAttribute(entry.entity)}" placeholder="sensor.example" autocapitalize="none" autocomplete="off" autocorrect="off" spellcheck="false">`;
}
_renderEntitySourceInput(kind, index, value, placeholder = "sensor.example", extraDataset = {}) {
const extraAttrs = Object.entries(extraDataset).map(([key, entry]) => `data-${key}="${this._escapeAttribute(entry)}"`).join(" ");
if (customElements.get("ha-entity-picker")) {
return `<ha-entity-picker data-kind="${kind}" data-index="${index}"${extraAttrs ? ` ${extraAttrs}` : ""}></ha-entity-picker>`;
}
return `<input type="text" data-kind="${kind}" data-index="${index}"${extraAttrs ? ` ${extraAttrs}` : ""} value="${this._escapeAttribute(value)}" placeholder="${this._escapeAttribute(placeholder)}" autocapitalize="none" autocomplete="off" autocorrect="off" spellcheck="false">`;
}
_hasMarkersOverride(scope) {
return (scope == null ? void 0 : scope.type) === "entity" && this._getScopedValue(scope, ["markers"]) !== void 0;
}
_getGenericMarkers(scope, effective = true) {
const local = this._getScopedValue(scope, ["markers"]);
if ((scope == null ? void 0 : scope.type) === "entity" && local === void 0 && effective) {
const cardMarkers = this._getScopedValue({ type: "card" }, ["markers"]);
return Array.isArray(cardMarkers) ? this._cloneDeep(cardMarkers) : [];
}
return Array.isArray(local) ? this._cloneDeep(local) : [];
}
_getGenericMarkersSummary(scope) {
if ((scope == null ? void 0 : scope.type) === "entity" && !this._hasMarkersOverride(scope)) return "Inherited";
const count = this._getGenericMarkers(scope, false).length;
return count === 0 ? "No reference markers" : `${count} reference marker${count === 1 ? "" : "s"}`;
}
_getGenericMarkerScopeKey(scope) {
return (scope == null ? void 0 : scope.type) === "entity" ? `entity:${scope.index}` : "card";
}
_getGenericMarkerUiIds(scope, count) {
var _a;
const key = this._getGenericMarkerScopeKey(scope);
const ids = (_a = this._genericMarkerUiIds.get(key)) != null ? _a : [];
while (ids.length < count) {
ids.push(`marker-${++this._nextGenericMarkerUiId}`);
}
ids.length = count;
this._genericMarkerUiIds.set(key, ids);
return ids;
}
_resetGenericMarkerUiScope(scope) {
var _a;
const key = this._getGenericMarkerScopeKey(scope);
const ids = (_a = this._genericMarkerUiIds.get(key)) != null ? _a : [];
ids.forEach((id) => this._expandedGenericMarkerUiIds.delete(id));
this._genericMarkerUiIds.delete(key);
}
_getGenericMarkerSummary(marker) {
var _a;
const source = this._getGenericMarkerSource(marker);
const lane = (marker == null ? void 0 : marker.lane) === "above" ? "Above" : "Below";
const shape = (_a = marker == null ? void 0 : marker.shape) != null ? _a : "circle";
let sourceSummary;
if (source.mode === "percent") {
sourceSummary = source.percent === "" ? "Percentage" : `${source.percent}%`;
} else if (source.mode === "entity" || source.mode === "entity-fallback") {
sourceSummary = source.entity || "Entity";
} else {
sourceSummary = source.fixed === "" ? "Fixed value" : String(source.fixed);
}
return `${lane} \xB7 ${shape.charAt(0).toUpperCase()}${shape.slice(1)} \xB7 ${sourceSummary}`;
}
_refreshGenericMarkerSummary(scope, markerIndex) {
const markers = this._getGenericMarkers(scope);
const marker = markers[markerIndex];
const uiId = this._getGenericMarkerUiIds(scope, markers.length)[markerIndex];
const summary = this._getShadowElementById(`generic-${uiId}-summary`);
if (marker && summary) {
const text = this._getGenericMarkerSummary(marker);
summary.textContent = text;
summary.setAttribute("title", text);
}
}
_getGenericMarkerSource(marker) {
var _a, _b, _c;
const at = marker == null ? void 0 : marker.at;
if (typeof at === "string" && /^\s*[+-]?(?:\d+(?:\.\d+)?|\.\d+)\s*%\s*$/.test(at)) {
return { mode: "percent", percent: at.replace(/%/g, "").trim() };
}
if (typeof at === "string" && /^[a-z0-9_]+\.[a-z0-9_]+$/i.test(at.trim())) {
return { mode: "entity", entity: at.trim(), fixed: "" };
}
const source = this._isObject(at) ? at : {};
if (Object.prototype.hasOwnProperty.call(source, "entity")) {
return {
mode: source.fixed !== void 0 && source.fixed !== null && source.fixed !== "" ? "entity-fallback" : "entity",
entity: (_a = source.entity) != null ? _a : "",
fixed: (_b = source.fixed) != null ? _b : ""
};
}
return { mode: "fixed", fixed: (_c = source.fixed) != null ? _c : "" };
}
_renderGenericMarkersEditor(scope) {
const scopeType = scope.type;
const scopeIndex = scopeType === "entity" ? scope.index : "card";
const markers = this._getGenericMarkers(scope);
const markerUiIds = this._getGenericMarkerUiIds(scope, markers.length);
const override = this._hasMarkersOverride(scope);
const rows = markers.map((marker, markerIndex) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
const source = this._getGenericMarkerSource(marker);
const markerUiId = markerUiIds[markerIndex];
const rowId = `${scopeType}-${scopeIndex}-generic-${markerUiId}`;
const expanded = this._expandedGenericMarkerUiIds.has(markerUiId);
const entitySource = source.mode === "entity" || source.mode === "entity-fallback" ? this._renderEntitySourceInput("generic-marker-entity", scopeIndex, source.entity, "sensor.reference", {
"scope-type": scopeType,
"marker-index": markerIndex
}) : "";
const labelEntitySource = this._renderEntitySourceInput(
"generic-marker-label-entity",
scopeIndex,
(_b = (_a = marker == null ? void 0 : marker.label) == null ? void 0 : _a.entity) != null ? _b : "",
"sensor.information",
{
"scope-type": scopeType,
"marker-index": markerIndex
}
);
return `
<div class="generic-marker-item" data-marker-ui-id="${markerUiId}" data-expanded="${expanded ? "true" : "false"}">
<div class="generic-marker-header">
<button type="button" class="generic-marker-toggle" data-action="toggle-generic-marker" data-marker-ui-id="${markerUiId}" aria-expanded="${expanded ? "true" : "false"}">
<span class="generic-marker-title">Reference marker ${markerIndex + 1}</span>
<span id="generic-${markerUiId}-summary" class="generic-marker-summary" title="${this._escapeAttribute(this._getGenericMarkerSummary(marker))}">${this._escapeAttribute(this._getGenericMarkerSummary(marker))}</span>
</button>
<div class="generic-marker-actions">
<button type="button" data-action="move-generic-marker-up" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}"${markerIndex === 0 ? " disabled" : ""} aria-label="Move marker up">\u2191</button>
<button type="button" data-action="move-generic-marker-down" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}"${markerIndex === markers.length - 1 ? " disabled" : ""} aria-label="Move marker down">\u2193</button>
<button type="button" data-action="remove-generic-marker" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}" aria-label="Remove marker">Remove</button>
</div>
</div>
<div class="generic-marker-body" style="display:${expanded ? "grid" : "none"};">
<div class="field-row">
<label for="${rowId}-source-mode">Source</label>
<select id="${rowId}-source-mode" data-kind="generic-marker-source-mode" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}">
<option value="fixed"${source.mode === "fixed" ? " selected" : ""}>Fixed</option>
<option value="entity"${source.mode === "entity" ? " selected" : ""}>Entity</option>
<option value="entity-fallback"${source.mode === "entity-fallback" ? " selected" : ""}>Entity with fixed fallback</option>
<option value="percent"${source.mode === "percent" ? " selected" : ""}>Percentage</option>
</select>
</div>
${source.mode === "fixed" ? `
<div class="field-row">
<label for="${rowId}-fixed">Fixed value</label>
<input id="${rowId}-fixed" type="number" step="any" data-kind="generic-marker-fixed" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}" value="${this._escapeAttribute(source.fixed)}">
</div>` : ""}
${source.mode === "entity" || source.mode === "entity-fallback" ? `
<div class="field-row">
<label>Reference marker entity</label>
${entitySource}
</div>` : ""}
${source.mode === "entity-fallback" ? `
<div class="field-row">
<label for="${rowId}-fallback">Fixed fallback</label>
<input id="${rowId}-fallback" type="number" step="any" data-kind="generic-marker-fallback" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}" value="${this._escapeAttribute(source.fixed)}">
</div>` : ""}
${source.mode === "percent" ? `
<div class="field-row">
<label for="${rowId}-percent">Scale percentage</label>
<input id="${rowId}-percent" type="number" min="0" max="100" step="any" data-kind="generic-marker-percent" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}" value="${this._escapeAttribute(source.percent)}">%
</div>` : ""}
<div class="inline-row generic-marker-pair">
<div class="field-row">
<label for="${rowId}-lane">Lane</label>
<select id="${rowId}-lane" data-kind="generic-marker-lane" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}">
<option value="above"${(marker == null ? void 0 : marker.lane) === "above" ? " selected" : ""}>Above</option>
<option value="below"${((_c = marker == null ? void 0 : marker.lane) != null ? _c : "below") === "below" ? " selected" : ""}>Below</option>
</select>
</div>
<div class="field-row">
<label for="${rowId}-direction">Direction</label>
<select id="${rowId}-direction" data-kind="generic-marker-direction" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}" value="${normalizeMarkerDirection(marker == null ? void 0 : marker.direction)}">
<option value="inward"${((_d = marker == null ? void 0 : marker.direction) != null ? _d : "inward") === "inward" ? " selected" : ""}>Inward</option>
<option value="outward"${(marker == null ? void 0 : marker.direction) === "outward" ? " selected" : ""}>Outward</option>
</select>
</div>
</div>
<div class="field-row">
<label for="${rowId}-shape">Shape</label>
<select id="${rowId}-shape" data-kind="generic-marker-shape" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}">
${["circle", "diamond", "triangle", "chevron", "arrow", "pin"].map((shape) => {
var _a2;
return `<option value="${shape}"${((_a2 = marker == null ? void 0 : marker.shape) != null ? _a2 : "circle") === shape ? " selected" : ""}>${shape}</option>`;
}).join("")}
</select>
</div>
<div class="field-row">
<label for="${rowId}-color">Color</label>
${this._renderColorInput({
id: `${rowId}-color`,
kind: "generic-marker-color",
index: scopeIndex,
value: (_e = marker == null ? void 0 : marker.color) != null ? _e : "#888888",
fallbackHex: "#888888",
placeholder: "#888888",
extraDataset: { "scope-type": scopeType, "marker-index": markerIndex }
})}
</div>
<div class="field-row"><div class="toggle">
<input id="${rowId}-show-marker" type="checkbox" data-kind="generic-marker-show-marker" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}"${(marker == null ? void 0 : marker.show_marker) === false ? "" : " checked"}>
<label for="${rowId}-show-marker">Show marker shape</label>
</div></div>
<div class="field-row"><div class="toggle">
<input id="${rowId}-label-show" type="checkbox" data-kind="generic-marker-label-show" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}"${((_f = marker == null ? void 0 : marker.label) == null ? void 0 : _f.show) === true ? " checked" : ""}>
<label for="${rowId}-label-show">Show label</label>
</div></div>
<div class="field-row"><label for="${rowId}-label-text">Label text</label>
<input id="${rowId}-label-text" type="text" data-kind="generic-marker-label-text" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}" value="${this._escapeAttribute((_h = (_g = marker == null ? void 0 : marker.label) == null ? void 0 : _g.text) != null ? _h : "")}" placeholder="optional semantic text">
</div>
<div class="field-row">
<label>Label content entity</label>
${labelEntitySource}
</div>
<div class="inline-row generic-marker-options">
<div class="field-row"><div class="toggle">
<input id="${rowId}-label-show-value" type="checkbox" data-kind="generic-marker-label-show-value" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}"${((_i = marker == null ? void 0 : marker.label) == null ? void 0 : _i.show_value) === false ? "" : " checked"}>
<label for="${rowId}-label-show-value">Show value</label>
</div></div>
<div class="field-row"><div class="toggle">
<input id="${rowId}-label-show-unit" type="checkbox" data-kind="generic-marker-label-show-unit" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}"${((_j = marker == null ? void 0 : marker.label) == null ? void 0 : _j.show_unit) === false ? "" : " checked"}>
<label for="${rowId}-label-show-unit">Show raw unit</label>
</div></div>
<div class="field-row generic-marker-precision"><label for="${rowId}-label-precision">Label precision</label>
<input id="${rowId}-label-precision" type="number" min="0" step="1" data-kind="generic-marker-label-precision" data-scope-type="${scopeType}" data-index="${scopeIndex}" data-marker-index="${markerIndex}" value="${this._escapeAttribute((_n = (_m = (_k = marker == null ? void 0 : marker.label) == null ? void 0 : _k.precision) != null ? _m : (_l = marker == null ? void 0 : marker.label) == null ? void 0 : _l.decimal) != null ? _n : "")}" placeholder="inherit">
</div>
</div>
</div>
</div>`;
}).join("");
const inheritControl = scopeType === "entity" ? `
<div class="field-row">
<div class="toggle">
<input id="entity-${scopeIndex}-markers-inherit" type="checkbox" data-kind="entity-markers-inherit" data-index="${scopeIndex}"${override ? "" : " checked"}>
<label for="entity-${scopeIndex}-markers-inherit">Inherit card markers</label>
</div>
</div>` : "";
const overrideNote = scopeType === "entity" && !override ? '<div class="section-note">Enable the override to replace the card marker list. An empty override clears all card markers.</div>' : "";
return `
${inheritControl}
${overrideNote}
${scopeType === "card" || override ? `
<div class="section-note">Up to four markers render in each lane, including Peak, Floor, and Target. Excess generic markers remain editable and show a warning. Unresolved markers still reserve a slot and lane.</div>
<div class="list generic-marker-list">${rows}</div>
<button type="button" data-action="add-generic-marker" data-scope-type="${scopeType}" data-index="${scopeIndex}">Add reference marker</button>` : ""}
`;
}
_getGenericMarkerScope(target) {
var _a;
return ((_a = target == null ? void 0 : target.dataset) == null ? void 0 : _a.scopeType) === "entity" ? { type: "entity", index: Number(target.dataset.index) } : { type: "card" };
}
_setGenericMarkerList(scope, markers, options = {}) {
return this._setScopedValue(scope, ["markers"], markers, { rerender: false, ...options });
}
_updateGenericMarker(scope, markerIndex, update, options = {}) {
var _a;
const markers = this._getGenericMarkers(scope);
if (!markers[markerIndex]) return false;
const marker = this._isObject(markers[markerIndex]) ? this._cloneDeep(markers[markerIndex]) : {};
const nextMarker = (_a = update(marker)) != null ? _a : marker;
markers[markerIndex] = nextMarker;
const changed = this._setGenericMarkerList(scope, markers, options);
if (changed) this._refreshGenericMarkerSummary(scope, markerIndex);
return changed;
}
_setGenericMarkerSourceMode(scope, markerIndex, mode) {
return this._updateGenericMarker(scope, markerIndex, (marker) => {
if (mode === "percent") {
marker.at = "50%";
return marker;
}
const oldAt = this._isObject(marker.at) ? this._cloneDeep(marker.at) : {};
if (mode === "fixed") {
delete oldAt.entity;
delete oldAt.percent;
if (oldAt.fixed === void 0) oldAt.fixed = 50;
} else {
delete oldAt.percent;
if (!oldAt.entity) oldAt.entity = "";
if (mode === "entity") delete oldAt.fixed;
if (mode === "entity-fallback" && oldAt.fixed === void 0) oldAt.fixed = 50;
}
marker.at = oldAt;
return marker;
}, { rerender: true });
}
_setGenericMarkerField(scope, markerIndex, kind, value) {
var _a, _b;
const atLeaf = (key, nextValue) => this._updateGenericMarker(scope, markerIndex, (marker) => {
const at = this._isObject(marker.at) ? this._cloneDeep(marker.at) : {};
if (nextValue === void 0) delete at[key];
else at[key] = nextValue;
marker.at = Object.keys(at).length ? at : null;
return marker;
});
if (kind === "generic-marker-source-mode") return this._setGenericMarkerSourceMode(scope, markerIndex, value);
if (kind === "generic-marker-show-marker") {
return this._updateGenericMarker(scope, markerIndex, (marker) => {
if (value === false) marker.show_marker = false;
else delete marker.show_marker;
return marker;
});
}
if (kind === "generic-marker-fixed") {
return atLeaf("fixed", (_a = this._normalizeNumberValue(value)) != null ? _a : void 0);
}
if (kind === "generic-marker-fallback") {
return atLeaf("fixed", (_b = this._normalizeNumberValue(value)) != null ? _b : void 0);
}
if (kind === "generic-marker-entity") {
return atLeaf("entity", this._normalizeTextValue(value).trim() || void 0);
}
if (kind === "generic-marker-percent") {
const percent = this._normalizeNumberValue(value);
return this._updateGenericMarker(scope, markerIndex, (marker) => {
marker.at = percent === null ? null : `${percent}%`;
return marker;
});
}
if (kind === "generic-marker-lane" || kind === "generic-marker-shape" || kind === "generic-marker-direction") {
return this._updateGenericMarker(scope, markerIndex, (marker) => {
const key = kind === "generic-marker-lane" ? "lane" : kind === "generic-marker-shape" ? "shape" : "direction";
marker[key] = key === "direction" ? normalizeMarkerDirection(value) : value;
return marker;
});
}
if (kind === "generic-marker-color") {
return this._updateGenericMarker(scope, markerIndex, (marker) => {
marker.color = this._normalizeTextValue(value).trim();
return marker;
});
}
if (kind.startsWith("generic-marker-label-")) {
return this._updateGenericMarker(scope, markerIndex, (marker) => {
const label = this._isObject(marker.label) ? this._cloneDeep(marker.label) : {};
if (kind === "generic-marker-label-show") label.show = value === true;
if (kind === "generic-marker-label-text") {
const text = this._normalizeTextValue(value).replace(/\s+/g, " ").trim();
if (text) label.text = text;
else delete label.text;
}
if (kind === "generic-marker-label-entity") {
const entity = this._normalizeTextValue(value).trim();
if (entity) label.entity = entity;
else delete label.entity;
}
if (kind === "generic-marker-label-show-value") label.show_value = value === true;
if (kind === "generic-marker-label-show-unit") label.show_unit = value === true;
if (kind === "generic-marker-label-precision") {
const precision = this._normalizeDecimalValue(value);
if (precision === null) {
delete label.precision;
delete label.decimal;
} else {
label.precision = precision;
delete label.decimal;
}
}
marker.label = label;
return marker;
});
}
return false;
}
_cleanupGenericMarkersForEmit(target) {
if (!this._isObject(target) || !Array.isArray(target.markers)) return target;
const nextTarget = this._cloneDeep(target);
nextTarget.markers = nextTarget.markers.map((rawMarker) => {
var _a;
if (!this._isObject(rawMarker)) return rawMarker;
const marker = this._cloneDeep(rawMarker);
if (marker.show_marker === true) delete marker.show_marker;
if (this._isObject(marker.at)) {
const at = this._cloneDeep(marker.at);
const fixed = this._normalizeNumberValue(at.fixed);
const entity = this._normalizeTextValue(at.entity).trim();
if (fixed !== null) at.fixed = fixed;
else delete at.fixed;
if (entity) at.entity = entity;
else delete at.entity;
if (Object.keys(at).length) marker.at = at;
else delete marker.at;
}
if (marker.lane === "below") delete marker.lane;
if (marker.shape === "circle") delete marker.shape;
if (marker.direction === "inward") delete marker.direction;
if (this._normalizeColorComparisonValue(marker.color) === this._normalizeColorComparisonValue("#888888")) delete marker.color;
if (this._isObject(marker.label)) {
const label = this._cloneDeep(marker.label);
if (label.show === false) delete label.show;
if (label.show_value === true) delete label.show_value;
if (label.show_unit === true) delete label.show_unit;
if (typeof label.text === "string") {
label.text = label.text.replace(/\s+/g, " ").trim();
if (!label.text) delete label.text;
}
const precision = this._normalizeDecimalValue((_a = label.precision) != null ? _a : label.decimal);
delete label.decimal;
if (precision === null) delete label.precision;
else label.precision = precision;
delete label.unit;
if (Object.keys(label).length) marker.label = label;
else delete marker.label;
}
return marker;
});
return nextTarget;
}
_renderResetOptions(value) {
const selected = this._normalizeTextValue(value).trim().toLowerCase() || "never";
const options = [
"never",
"quarterly",
"hourly",
"daily",
"weekly",
"monthly",
"yearly",
...Array.from({ length: 59 }, (_, index) => `${index + 1}m`),
...Array.from({ length: 23 }, (_, index) => `${index + 1}h`)
];
return options.map((option) => `<option value="${option}"${selected === option ? " selected" : ""}>${option}</option>`).join("");
}
_escapeAttribute(value) {
return this._normalizeTextValue(value).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
_isHexColorValue(value) {
return typeof value === "string" && /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value.trim());
}
_expandHexColor(value) {
if (!this._isHexColorValue(value)) {
return null;
}
const normalized = value.trim().toLowerCase();
if (normalized.length === 7) {
return normalized;
}
return `#${normalized.slice(1).split("").map((char) => char + char).join("")}`;
}
_normalizeColorComparisonValue(value) {
var _a;
const normalizedText = this._normalizeTextValue(value).trim().toLowerCase();
if (!normalizedText) {
return "";
}
return (_a = this._expandHexColor(normalizedText)) != null ? _a : normalizedText;
}
_getColorPickerValue(value, fallbackHex = "#000000") {
var _a, _b;
return (_b = (_a = this._expandHexColor(value)) != null ? _a : this._expandHexColor(fallbackHex)) != null ? _b : "#000000";
}
_renderColorInput({ id, field = null, kind = null, index = null, value = "", fallbackHex = "#000000", placeholder = "", extraDataset = {} }) {
const controlValue = this._normalizeTextValue(value).trim();
const pickerValue = this._getColorPickerValue(controlValue, fallbackHex);
const extraAttrs = Object.entries(extraDataset).map(([key, entry]) => `data-${key}="${this._escapeAttribute(entry)}"`).join(" ");
const baseAttrs = field ? `data-field="${field}"${extraAttrs ? ` ${extraAttrs}` : ""}` : `data-kind="${kind}" data-index="${index}"${extraAttrs ? ` ${extraAttrs}` : ""}`;
const fallbackAttrs = field ? `data-field="${field}-text-fallback"${extraAttrs ? ` ${extraAttrs}` : ""}` : `data-kind="${kind}-text-fallback" data-index="${index}"${extraAttrs ? ` ${extraAttrs}` : ""}`;
return `
<div class="field-grid">
<input id="${id}" type="color" ${baseAttrs} value="${this._escapeAttribute(pickerValue)}">
${controlValue && !this._isHexColorValue(controlValue) ? `<input type="text" ${fallbackAttrs} value="${this._escapeAttribute(controlValue)}" placeholder="${this._escapeAttribute(placeholder || "CSS color value")}">` : ""}
</div>
`;
}
_renderListRows(items, renderItem) {
return items.map((item, index) => renderItem(item, index)).join("");
}
_render() {
var _a;
if (!this.shadowRoot || this._isRendering) return;
this._isRendering = true;
try {
const entities = this._getEntitiesValue();
const fillStyle = this._getFillStyleValue();
const layoutLabelPosition = this._getScopedLayoutValue({ type: "card" }, "position") || "left";
const layoutHeroSize = this._getScopedLayoutValue({ type: "card" }, "hero_size") || "medium";
const layoutHeroValueSize = this._getScopedLayoutValue({ type: "card" }, "value_size");
const layoutHeight = this._getScopedLayoutValue({ type: "card" }, "height");
const layoutLabelWidth = this._getScopedLayoutValue({ type: "card" }, "width");
const barColor = this._getScopedBarColorValue({ type: "card" });
const barSolidFill = this._getScopedBarSolidFillValue({ type: "card" });
const cardNeedle = this._getScopedNeedleConfig({ type: "card" });
const gradientStops = this._getGradientStopsValue();
const gradientDraft = this._getGradientStopsDraftState({ type: "card" });
const gradientDraftMessage = this._getGradientDraftValidationMessage({ type: "card" });
const segments = this._getSegmentsValue();
const baseline = this._getBaselineResolvableValue({ type: "card" });
const baselineMode = this._getBaselineMode({ type: "card" });
const baselineAboveColor = this._getBaselineDirectionalColorValue({ type: "card" }, "above");
const baselineBelowColor = this._getBaselineDirectionalColorValue({ type: "card" }, "below");
const target = this._getTargetResolvableValue({ type: "card" });
const targetMode = this._getTargetMode({ type: "card" });
const targetShape = this._getEffectiveTargetShapeValue({ type: "card" });
const targetDirection = this._getEffectiveMarkerDirection({ type: "card" }, "target");
const targetColor = this._getTargetColorValue({ type: "card" });
const targetLabelShow = this._getTargetLabelShowValue({ type: "card" });
const targetLabelDecimal = this._getTargetLabelDecimalValue({ type: "card" });
const targetAboveFillColor = this._getTargetAboveFillColorValue({ type: "card" });
const formattingUnit = this._getScopedFormattingValue({ type: "card" }, "unit");
const formattingDecimal = this._getScopedFormattingValue({ type: "card" }, "decimal");
const cardPeak = this._getScopedPeakConfig({ type: "card" });
const cardPeakExtras = this._getEffectiveMarkerExtras({ type: "card" }, "peak");
const cardFloor = this._getEffectiveScopedFloorConfig({ type: "card" });
const scaleMin = this._getScaleFixedValue("min", "min");
const scaleMax = this._getScaleFixedValue("max", "max");
const scaleMinEntity = this._getScaleEntityValue("min");
const scaleMaxEntity = this._getScaleEntityValue("max");
const gradientStopsSummary = this._getGradientStopsSummary({ type: "card" });
const gradientStopsInactive = this._getEffectiveFillStyleValue({ type: "card" }) !== "gradient";
const defaultSegmentsVisible = !this._hasSegmentsOverride({ type: "card" }) && this._isSegmentFillStyle(this._getEffectiveFillStyleValue({ type: "card" }));
this._syncExpandedEntityOverrides(entities.length);
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
}
.editor {
display: grid;
gap: 18px;
padding: 10px 0 14px;
}
.section {
display: grid;
gap: 14px;
padding: 14px;
background: color-mix(in srgb, var(--card-background-color, #fff) 88%, transparent);
border: 1px solid color-mix(in srgb, var(--divider-color, #888) 35%, transparent);
border-radius: 14px;
box-shadow: 0 1px 0 color-mix(in srgb, var(--divider-color, #888) 20%, transparent);
}
.section-head {
display: grid;
gap: 4px;
}
.section h3 {
margin: 0;
font-size: 1rem;
font-weight: 700;
letter-spacing: 0.01em;
color: var(--primary-text-color, #111);
}
.section-note {
font-size: 0.82rem;
color: var(--secondary-text-color, #666);
}
.field-grid {
display: grid;
gap: 12px;
}
.editor-grid,
.field-row {
display: grid;
gap: 8px;
min-width: 0;
}
.inline-row {
display: grid;
gap: 12px;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
align-items: end;
}
label {
font-size: 0.9rem;
font-weight: 500;
color: var(--primary-text-color, #111);
min-width: 0;
}
input,
select,
button {
font: inherit;
}
input[type="text"],
input[type="number"],
input[type="color"],
select {
width: 100%;
box-sizing: border-box;
min-height: 42px;
padding: 8px 10px;
color: var(--primary-text-color, #111);
background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #fff)) 82%, transparent);
border: 1px solid color-mix(in srgb, var(--divider-color, #888) 38%, transparent);
border-radius: 10px;
}
input[type="color"] {
padding: 4px;
cursor: pointer;
}
input[type="text"]:focus,
input[type="number"]:focus,
input[type="color"]:focus,
select:focus {
outline: 2px solid color-mix(in srgb, var(--accent-color, var(--primary-color, #03a9f4)) 55%, transparent);
outline-offset: 1px;
}
input[type="checkbox"] {
width: 18px;
height: 18px;
}
.toggle {
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
}
.list {
display: grid;
gap: 10px;
min-width: 0;
}
.list-row {
display: grid;
gap: 10px;
grid-template-columns: minmax(0, 1fr) auto;
align-items: end;
min-width: 0;
}
.generic-marker-list {
gap: 8px;
}
.generic-marker-item {
min-width: 0;
overflow: hidden;
border: 1px solid color-mix(in srgb, var(--divider-color, #888) 32%, transparent);
border-radius: 10px;
background: color-mix(in srgb, var(--card-background-color, #fff) 82%, transparent);
}
.generic-marker-item[data-expanded="true"] {
border-color: color-mix(in srgb, var(--accent-color, var(--primary-color, #03a9f4)) 38%, var(--divider-color, #888));
}
.generic-marker-header {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
padding: 6px 8px;
}
.generic-marker-toggle {
display: grid;
grid-template-columns: minmax(0, auto) minmax(0, 1fr);
align-items: center;
gap: 4px 10px;
flex: 1 1 auto;
min-width: 0;
min-height: 40px;
padding: 4px 6px;
border: 0;
background: transparent;
text-align: left;
}
.generic-marker-toggle:hover,
.generic-marker-toggle:focus {
background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #fff)) 72%, transparent);
}
.generic-marker-title {
font-weight: 700;
white-space: nowrap;
}
.generic-marker-summary {
min-width: 0;
overflow: hidden;
color: var(--secondary-text-color, #666);
text-overflow: ellipsis;
white-space: nowrap;
}
.generic-marker-actions {
display: flex;
flex: 0 0 auto;
gap: 4px;
align-items: center;
}
.generic-marker-actions button {
min-width: 38px;
min-height: 38px;
padding: 6px;
}
.generic-marker-body {
gap: 12px;
padding: 12px;
border-top: 1px solid color-mix(in srgb, var(--divider-color, #888) 25%, transparent);
background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #fff)) 78%, transparent);
}
.generic-marker-pair,
.generic-marker-options {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
align-items: end;
}
.generic-marker-precision {
max-width: 220px;
}
.list-row.triple {
grid-template-columns: repeat(3, minmax(120px, 1fr)) auto;
}
.list-row.segment-row {
grid-template-columns: minmax(72px, 1fr) minmax(72px, 1fr) minmax(52px, 64px) 40px;
align-items: center;
}
.list-row.segment-row > * {
min-width: 0;
}
.list-row.segment-row > button {
width: 40px;
min-width: 40px;
padding: 6px;
justify-self: end;
}
.list-row.gradient-stop-row {
grid-template-columns: minmax(110px, 1fr) minmax(120px, 1fr) auto;
}
.gradient-stop-list {
display: grid;
gap: 10px;
min-width: 0;
}
.gradient-stop-draft {
padding-top: 10px;
border-top: 1px dashed color-mix(in srgb, var(--divider-color, #888) 34%, transparent);
}
.segment-editor-row {
display: grid;
gap: 6px;
min-width: 0;
}
.segment-draft {
display: grid;
gap: 6px;
padding-top: 10px;
border-top: 1px dashed color-mix(in srgb, var(--divider-color, #888) 34%, transparent);
}
.gradient-preview {
display: grid;
gap: 8px;
min-width: 0;
}
.gradient-preview-track {
position: relative;
width: 100%;
min-height: 28px;
min-width: 0;
border-radius: 999px;
border: 1px solid color-mix(in srgb, var(--divider-color, #888) 28%, transparent);
background: linear-gradient(to right, #4CAF50 0%, #FF9800 50%, #F44336 100%);
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--card-background-color, #fff) 35%, transparent);
overflow: hidden;
}
.gradient-preview-stop {
position: absolute;
top: 3px;
bottom: 3px;
width: 2px;
margin-left: -1px;
border-radius: 999px;
background: color-mix(in srgb, var(--primary-text-color, #111) 72%, transparent);
box-shadow: 0 0 0 1px color-mix(in srgb, var(--card-background-color, #fff) 52%, transparent);
}
.entity-shell {
display: grid;
gap: 10px;
padding: 12px;
min-width: 0;
background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #fff)) 68%, transparent);
border: 1px solid color-mix(in srgb, var(--divider-color, #888) 30%, transparent);
border-radius: 14px;
}
.entity-main {
display: grid;
gap: 10px;
padding: 10px 12px 12px;
min-width: 0;
border-radius: 12px;
background: color-mix(in srgb, var(--card-background-color, #fff) 90%, transparent);
border: 1px solid color-mix(in srgb, var(--divider-color, #888) 22%, transparent);
}
.entity-header {
display: grid;
gap: 12px;
grid-template-columns: minmax(0, 1fr) auto;
align-items: start;
padding-bottom: 4px;
border-bottom: 1px solid color-mix(in srgb, var(--divider-color, #888) 24%, transparent);
}
.entity-header-main {
display: grid;
gap: 4px;
min-width: 0;
flex: 1 1 auto;
}
.entity-title {
font-size: 0.95rem;
font-weight: 700;
color: var(--primary-text-color, #111);
}
.entity-subtitle {
font-size: 0.8rem;
color: var(--secondary-text-color, #666);
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.entity-actions {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
align-items: center;
gap: 8px;
flex: 0 0 auto;
min-width: min(100%, 240px);
}
.entity-actions button {
min-height: 34px;
padding: 6px 10px;
flex: 0 1 auto;
}
.entity-fields {
display: grid;
gap: 10px;
min-width: 0;
}
.override-toggle {
display: inline-flex;
align-items: center;
gap: 8px;
justify-self: start;
padding: 8px 10px;
border-radius: 10px;
border: 1px solid color-mix(in srgb, var(--divider-color, #888) 28%, transparent);
background: color-mix(in srgb, var(--card-background-color, #fff) 84%, transparent);
color: var(--secondary-text-color, #666);
transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.override-toggle:hover,
.override-toggle:focus {
background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #fff)) 74%, transparent);
border-color: color-mix(in srgb, var(--accent-color, var(--primary-color, #03a9f4)) 28%, var(--divider-color, #888));
color: var(--primary-text-color, #111);
}
.override-toggle[aria-expanded="true"] {
color: var(--primary-text-color, #111);
border-color: color-mix(in srgb, var(--accent-color, var(--primary-color, #03a9f4)) 40%, transparent);
box-shadow: inset 3px 0 0 color-mix(in srgb, var(--accent-color, var(--primary-color, #03a9f4)) 70%, transparent);
}
.override-panel {
display: grid;
gap: 12px;
padding: 12px 14px;
min-width: 0;
border-radius: 12px;
border: 1px solid color-mix(in srgb, var(--divider-color, #888) 28%, transparent);
border-left: 4px solid color-mix(in srgb, var(--accent-color, var(--primary-color, #03a9f4)) 65%, transparent);
background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #ffffff)) 82%, transparent);
}
.override-panel::before {
content: "Overrides";
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--secondary-text-color, #666);
}
.override-group {
display: grid;
gap: 10px;
min-width: 0;
--override-group-accent: var(--accent-color, var(--primary-color, #03a9f4));
border-radius: 12px;
border: 1px solid color-mix(in srgb, var(--divider-color, #888) 24%, transparent);
background: color-mix(in srgb, var(--card-background-color, #fff) 78%, transparent);
overflow: hidden;
}
.override-group[data-group="scale"] {
--override-group-accent: #4f8dff;
}
.override-group[data-group="target"] {
--override-group-accent: #ff9b3d;
}
.override-group[data-group="baseline"] {
--override-group-accent: #5bbd6d;
}
.override-group[data-group="bar"] {
--override-group-accent: #34c6d3;
}
.override-group[data-group="needle"] {
--override-group-accent: #9b6bff;
}
.override-group[data-group="formatting"] {
--override-group-accent: #d46be3;
}
.override-group[data-group="layout"] {
--override-group-accent: #6e90ff;
}
.override-group[data-group="peak"] {
--override-group-accent: #f08b3e;
}
.override-group[data-group="segments"] {
--override-group-accent: #d2a53a;
}
.override-group[data-group="gradient-stops"] {
--override-group-accent: #48b978;
}
.card-subgroup {
margin-top: 2px;
}
.override-group.is-inactive .override-group-summary,
.override-group.is-inactive .section-note {
color: var(--secondary-text-color, #666);
}
.override-group.is-inactive .override-group-body {
opacity: 0.92;
}
.override-group-toggle {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 12px;
width: 100%;
text-align: left;
border: 0;
border-radius: 0;
background: transparent;
padding: 10px 12px;
min-width: 0;
}
.override-group-toggle:hover,
.override-group-toggle:focus {
background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #fff)) 72%, transparent);
}
.override-group[data-expanded="true"] {
border-color: color-mix(in srgb, var(--override-group-accent) 34%, transparent);
box-shadow: inset 3px 0 0 color-mix(in srgb, var(--override-group-accent) 70%, transparent);
}
.override-group-title {
font-weight: 700;
color: var(--primary-text-color, #111);
min-width: 0;
overflow-wrap: anywhere;
}
.override-group-summary {
font-size: 0.82rem;
color: var(--secondary-text-color, #666);
text-align: right;
min-width: 0;
max-width: 42%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.override-group-body {
display: grid;
gap: 12px;
padding: 0 12px 12px;
min-width: 0;
background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #fff)) 78%, transparent);
}
button {
min-height: 40px;
padding: 8px 12px;
cursor: pointer;
color: var(--primary-text-color, #111);
background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #fff)) 78%, transparent);
border: 1px solid color-mix(in srgb, var(--divider-color, #888) 30%, transparent);
border-radius: 10px;
}
button:hover,
button:focus {
background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #fff)) 60%, transparent);
}
button:disabled {
cursor: not-allowed;
opacity: 0.58;
color: var(--secondary-text-color, #666);
background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #fff)) 86%, transparent);
border-color: color-mix(in srgb, var(--divider-color, #888) 22%, transparent);
box-shadow: none;
}
button:disabled:hover,
button:disabled:focus {
background: color-mix(in srgb, var(--secondary-background-color, var(--card-background-color, #fff)) 86%, transparent);
}
.field-row .field-grid {
gap: 8px;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
align-items: end;
}
.field-row .field-grid > * {
min-width: 0;
}
ha-entity-picker {
min-width: 0;
width: 100%;
}
@media (max-width: 720px) {
.section {
gap: 12px;
}
.inline-row {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.override-group-toggle {
flex-wrap: wrap;
}
.override-group-summary {
max-width: 100%;
flex: 1 1 100%;
text-align: left;
}
.entity-actions {
min-width: 0;
justify-content: flex-start;
}
.list-row.triple,
.list-row.gradient-stop-row {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.list-row.segment-row {
grid-template-columns: repeat(3, minmax(0, 1fr)) 40px;
}
.list-row.triple > button,
.list-row.gradient-stop-row > button {
grid-column: 1 / -1;
width: 100%;
}
.list-row.segment-row > button {
grid-column: auto;
width: 40px;
}
}
@media (max-width: 480px) {
.section {
padding: 12px;
}
.inline-row {
grid-template-columns: minmax(0, 1fr);
}
.entity-shell,
.entity-main,
.override-panel {
padding-left: 10px;
padding-right: 10px;
}
.entity-header {
grid-template-columns: minmax(0, 1fr);
}
.entity-actions {
width: 100%;
}
.entity-actions button {
flex: 1 1 120px;
}
.list-row {
grid-template-columns: minmax(0, 1fr);
}
.generic-marker-header {
flex-wrap: wrap;
}
.generic-marker-toggle {
flex-basis: 100%;
}
.generic-marker-actions {
width: 100%;
}
.generic-marker-actions button {
flex: 1 1 auto;
}
.generic-marker-pair,
.generic-marker-options {
grid-template-columns: minmax(0, 1fr);
}
.list-row.triple,
.list-row.gradient-stop-row {
grid-template-columns: minmax(0, 1fr);
}
.list-row.segment-row {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.list-row > button,
.list-row.triple > button,
.list-row.gradient-stop-row > button {
width: 100%;
}
.list-row.segment-row > button {
grid-column: 1 / -1;
width: 100%;
}
}
</style>
<div class="editor">
<div class="section">
<div class="section-head">
<h3>Basics</h3>
</div>
<div class="inline-row editor-grid">
<div class="field-row">
<label for="title">Title</label>
<input id="title" type="text" data-field="title" value="${this._escapeAttribute((_a = this._draftConfig.title) != null ? _a : "")}">
</div>
</div>
</div>
<div class="section">
<div class="section-head">
<h3>Entities</h3>
<div class="section-note">Overrides replace card defaults only for this entity.</div>
</div>
<div class="field-grid">
<div class="list">
${this._renderListRows(entities, (entry, index) => {
var _a2, _b;
return `
<div class="entity-shell" data-entity-shell-index="${index}">
<div class="entity-main">
<div class="entity-header">
<div class="entity-header-main">
<div class="entity-title">Entity ${index + 1}</div>
<div class="entity-subtitle">${this._escapeAttribute(entry.entity || "Configure entity")}</div>
</div>
<div class="entity-actions">
<button type="button" data-action="move-entity-up" data-index="${index}"${index === 0 ? " disabled" : ""} aria-label="Move entity ${index + 1} up">\u2191</button>
<button type="button" data-action="move-entity-down" data-index="${index}"${index === entities.length - 1 ? " disabled" : ""} aria-label="Move entity ${index + 1} down">\u2193</button>
<button type="button" data-action="duplicate-entity" data-index="${index}">Duplicate</button>
<button type="button" data-action="remove-entity" data-index="${index}"${entities.length <= 1 ? " disabled" : ""} aria-label="Remove" title="Remove">\u{1F5D1}</button>
</div>
</div>
<div class="entity-fields">
${this._renderEntityInput(entry, index)}
<input type="text" data-kind="entity-name" data-index="${index}" value="${this._escapeAttribute((_a2 = entry.name) != null ? _a2 : "")}" placeholder="Name">
<input type="text" data-kind="entity-icon" data-index="${index}" value="${this._escapeAttribute((_b = entry.icon) != null ? _b : "")}" placeholder="mdi:flash" autocapitalize="none" autocomplete="off" autocorrect="off" spellcheck="false">
</div>
</div>
<button type="button" class="override-toggle" data-action="toggle-entity-overrides" data-index="${index}" aria-expanded="${this._isEntityOverrideExpanded(index) ? "true" : "false"}">
${this._isEntityOverrideExpanded(index) ? "\u25BE" : "\u25B8"} Overrides
</button>
<div class="override-panel" style="display:${this._isEntityOverrideExpanded(index) ? "grid" : "none"};">
<div class="section-note">Overrides replace card defaults only for this entity.</div>
${(() => {
var _a3, _b2;
const scope = { type: "entity", index };
const minParts = this._getEffectiveResolvableScopedValue(scope, "min");
const maxParts = this._getEffectiveResolvableScopedValue(scope, "max");
const barAppearanceInherited = !this._hasEntityBarAppearanceOverride(scope);
const needleInherited = !this._hasNeedleOverride(scope);
const entityNeedle = this._getEffectiveScopedNeedleConfig(scope);
const baselineInherited = !this._hasBaselineOverride(scope);
const baselineParts = this._getEffectiveBaselineResolvableValue(scope);
const baselineMode2 = this._getEffectiveBaselineMode(scope);
const targetInherited = !this._hasTargetOverride(scope);
const targetParts = this._getEffectiveTargetResolvableValue(scope);
const targetMode2 = this._getEffectiveTargetMode(scope);
const targetShape2 = this._getEffectiveTargetShapeValue(scope);
const targetDirection2 = this._getEffectiveMarkerDirection(scope, "target");
const formattingInherited = !this._hasFormattingOverride(scope);
const layoutInherited = !this._hasLayoutOverride(scope);
const peakInherited = !this._hasPeakOverride(scope);
const gradientStopsInherited = !this._hasGradientStopsOverride(scope);
const segmentsInherited = !this._hasSegmentsOverride(scope);
const scaleInherited = !this._hasResolvableOverride(this._getResolvableScopedValue(scope, "min")) && !this._hasResolvableOverride(this._getResolvableScopedValue(scope, "max"));
const entityPeak = this._getEffectiveScopedPeakConfig(scope);
const entityPeakExtras = this._getEffectiveMarkerExtras(scope, "peak");
const floorInherited = !this._hasExtremumOverride(scope, "floor");
const entityFloor = this._getEffectiveScopedFloorConfig(scope);
const entityGradientStops = this._getScopedGradientStopsValue(scope);
const entityGradientDraft = this._getGradientStopsDraftState(scope);
const entityGradientDraftMessage = this._getGradientDraftValidationMessage(scope);
const entitySegments = this._getScopedSegmentsValue(scope);
const scaleGroup = this._renderOverrideGroup({
index,
group: "scale",
title: "Scale",
summary: this._getScaleOverrideSummary(scope),
content: `
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-scale-inherit" type="checkbox" data-kind="entity-scale-inherit" data-index="${index}"${scaleInherited ? " checked" : ""}>
<label for="entity-${index}-scale-inherit">Inherit card settings</label>
</div>
</div>
<div class="section-note">Fixed values are used as fallback when entity values are unavailable.</div>
<div class="field-row">
<label for="entity-${index}-min">Min fallback</label>
<input id="entity-${index}-min" type="number" step="any" data-kind="entity-override-min" data-index="${index}" value="${this._escapeAttribute(minParts.fixed)}" placeholder="inherit card default">
</div>
<div class="field-row">
<label>Min entity</label>
${this._renderEntitySourceInput("entity-override-min-entity-source", index, minParts.entity, "inherit card default")}
</div>
<div class="field-row">
<label for="entity-${index}-max">Max fallback</label>
<input id="entity-${index}-max" type="number" step="any" data-kind="entity-override-max" data-index="${index}" value="${this._escapeAttribute(maxParts.fixed)}" placeholder="inherit card default">
</div>
<div class="field-row">
<label>Max entity</label>
${this._renderEntitySourceInput("entity-override-max-entity-source", index, maxParts.entity, "inherit card default")}
</div>
`
});
const layoutGroup = this._renderOverrideGroup({
index,
group: "layout",
title: "Layout",
summary: this._getLayoutSummary(scope),
content: `
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-layout-inherit" type="checkbox" data-kind="entity-layout-inherit" data-index="${index}"${layoutInherited ? " checked" : ""}>
<label for="entity-${index}-layout-inherit">Inherit card settings</label>
</div>
</div>
<div class="field-row">
<label for="entity-${index}-height">Row height</label>
<input id="entity-${index}-height" type="number" min="24" step="1" data-kind="entity-override-height" data-index="${index}" value="${this._escapeAttribute(this._getEffectiveScopedLayoutValue(scope, "height"))}" placeholder="inherit card default">
</div>
<div class="field-row">
<label for="entity-${index}-label-position">Label position</label>
<select id="entity-${index}-label-position" data-kind="entity-layout-label-position" data-index="${index}" value="${this._escapeAttribute(this._getEffectiveScopedLayoutValue(scope, "position"))}">
<option value=""${this._getEffectiveScopedLayoutValue(scope, "position") === "" ? " selected" : ""}>inherit card default</option>
<option value="left"${this._getEffectiveScopedLayoutValue(scope, "position") === "left" ? " selected" : ""}>left</option>
<option value="above"${this._getEffectiveScopedLayoutValue(scope, "position") === "above" ? " selected" : ""}>above</option>
<option value="inside"${this._getEffectiveScopedLayoutValue(scope, "position") === "inside" ? " selected" : ""}>inside</option>
<option value="hero"${this._getEffectiveScopedLayoutValue(scope, "position") === "hero" ? " selected" : ""}>hero</option>
<option value="off"${this._getEffectiveScopedLayoutValue(scope, "position") === "off" ? " selected" : ""}>off</option>
</select>
</div>
${(this._getEffectiveScopedLayoutValue(scope, "position") || "") === "hero" ? `
<div class="field-row">
<label for="entity-${index}-label-hero-size">Hero size</label>
<select id="entity-${index}-label-hero-size" data-kind="entity-layout-label-hero-size" data-index="${index}" value="${this._escapeAttribute(this._getEffectiveScopedLayoutValue(scope, "hero_size") || "medium")}">
<option value="small"${(this._getEffectiveScopedLayoutValue(scope, "hero_size") || "medium") === "small" ? " selected" : ""}>small</option>
<option value="medium"${(this._getEffectiveScopedLayoutValue(scope, "hero_size") || "medium") === "medium" ? " selected" : ""}>medium</option>
<option value="large"${(this._getEffectiveScopedLayoutValue(scope, "hero_size") || "medium") === "large" ? " selected" : ""}>large</option>
</select>
</div>
<div class="field-row">
<label for="entity-${index}-hero-value-size">Maximum font size</label>
<input id="entity-${index}-hero-value-size" type="number" min="12" max="112" step="1" data-kind="entity-layout-hero-value-size" data-index="${index}" value="${this._escapeAttribute(this._getEffectiveScopedLayoutValue(scope, "value_size"))}" placeholder="use Hero size preset">
<div class="section-note">The hero value may render smaller when needed to fit. A custom value overrides the Hero size preset.</div>
</div>
` : ""}
<div class="field-row">
<label for="entity-${index}-label-width">Label width</label>
<input id="entity-${index}-label-width" type="number" step="1" data-kind="entity-layout-label-width" data-index="${index}" value="${this._escapeAttribute(this._getEffectiveScopedLayoutValue(scope, "width"))}" placeholder="inherit card default">
</div>
`
});
const barGroup = this._renderOverrideGroup({
index,
group: "bar",
title: "Bar Appearance",
summary: this._getBarAppearanceSummary(scope),
content: `
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-bar-inherit" type="checkbox" data-kind="entity-bar-inherit" data-index="${index}"${barAppearanceInherited ? " checked" : ""}>
<label for="entity-${index}-bar-inherit">Inherit card settings</label>
</div>
</div>
<div class="field-row">
<label for="entity-${index}-bar-fill-style">Fill style</label>
<select id="entity-${index}-bar-fill-style" data-kind="entity-bar-fill-style" data-index="${index}" value="${this._escapeAttribute(this._getEffectiveScopedFillStyleValue(scope))}">
<option value="bands"${this._getEffectiveScopedFillStyleValue(scope) === "bands" ? " selected" : ""}>bands</option>
<option value="solid"${this._getEffectiveScopedFillStyleValue(scope) === "solid" ? " selected" : ""}>solid</option>
<option value="gradient"${this._getEffectiveScopedFillStyleValue(scope) === "gradient" ? " selected" : ""}>gradient</option>
<option value="soft_bands"${this._getEffectiveScopedFillStyleValue(scope) === "soft_bands" ? " selected" : ""}>soft_bands</option>
<option value="band_gradient"${this._getEffectiveScopedFillStyleValue(scope) === "band_gradient" ? " selected" : ""}>band_gradient</option>
</select>
</div>
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-bar-solid-fill" type="checkbox" data-kind="entity-bar-solid-fill" data-index="${index}"${this._getEffectiveScopedBarSolidFillValue(scope) ? " checked" : ""}>
<label for="entity-${index}-bar-solid-fill">Solid fill</label>
</div>
</div>
<div class="field-row">
<label for="entity-${index}-bar-color">Bar color</label>
${this._renderColorInput({
id: `entity-${index}-bar-color`,
kind: "entity-bar-color",
index,
value: this._getEffectiveScopedBarColorValue(scope),
fallbackHex: "#4a9eff",
placeholder: "inherit card default"
})}
</div>
`
});
const needleGroup = this._renderOverrideGroup({
index,
group: "needle",
title: "Needle",
summary: this._getNeedleSummary(scope),
content: `
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-needle-inherit" type="checkbox" data-kind="entity-needle-inherit" data-index="${index}"${needleInherited ? " checked" : ""}>
<label for="entity-${index}-needle-inherit">Inherit card settings</label>
</div>
</div>
<div class="field-row">
<label for="entity-${index}-needle-mode">Needle mode</label>
<select id="entity-${index}-needle-mode" data-kind="entity-needle-mode" data-index="${index}" value="${this._escapeAttribute(entityNeedle.mode)}">
<option value="enabled"${entityNeedle.mode === "enabled" ? " selected" : ""}>enabled</option>
<option value="disabled"${entityNeedle.mode === "disabled" ? " selected" : ""}>disabled</option>
</select>
</div>
<div class="field-row">
<label for="entity-${index}-needle-color">Needle color</label>
${this._renderColorInput({
id: `entity-${index}-needle-color`,
kind: "entity-needle-color",
index,
value: entityNeedle.color,
fallbackHex: "#ffffff",
placeholder: "#ffffff"
})}
</div>
`
});
const formattingGroup = this._renderOverrideGroup({
index,
group: "formatting",
title: "Formatting",
summary: this._getFormattingSummary(scope),
content: `
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-formatting-inherit" type="checkbox" data-kind="entity-formatting-inherit" data-index="${index}"${formattingInherited ? " checked" : ""}>
<label for="entity-${index}-formatting-inherit">Inherit card settings</label>
</div>
</div>
<div class="field-row">
<label for="entity-${index}-formatting-unit">Unit</label>
<input id="entity-${index}-formatting-unit" type="text" data-kind="entity-formatting-unit" data-index="${index}" value="${this._escapeAttribute(this._getEffectiveScopedFormattingValue(scope, "unit"))}" placeholder="inherit card default">
</div>
<div class="field-row">
<label for="entity-${index}-formatting-decimal">Decimals</label>
<input id="entity-${index}-formatting-decimal" type="number" min="0" step="1" data-kind="entity-formatting-decimal" data-index="${index}" value="${this._escapeAttribute(this._getEffectiveScopedFormattingValue(scope, "decimal"))}" placeholder="inherit card default">
</div>
`
});
const peakGroup = this._renderOverrideGroup({
index,
group: "peak",
title: "Peak",
summary: this._getPeakSummary(scope),
content: `
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-peak-inherit" type="checkbox" data-kind="entity-peak-inherit" data-index="${index}"${peakInherited ? " checked" : ""}>
<label for="entity-${index}-peak-inherit">Inherit card settings</label>
</div>
</div>
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-peak-enabled" type="checkbox" data-kind="entity-peak-enabled" data-index="${index}"${entityPeak.mode === "enabled" ? " checked" : ""}>
<label for="entity-${index}-peak-enabled">Peak enabled</label>
</div>
</div>
<div class="field-row">
<label for="entity-${index}-peak-color">Peak color</label>
${this._renderColorInput({
id: `entity-${index}-peak-color`,
kind: "entity-peak-color",
index,
value: entityPeak.color,
fallbackHex: "#888",
placeholder: "inherit card default"
})}
</div>
<div class="field-row">
<label for="entity-${index}-peak-reset">Peak reset</label>
<select id="entity-${index}-peak-reset" data-kind="entity-peak-reset" data-index="${index}" value="${this._escapeAttribute((_a3 = entityPeakExtras.reset) != null ? _a3 : "never")}">
${this._renderResetOptions((_b2 = entityPeakExtras.reset) != null ? _b2 : "never")}
</select>
</div>
<div class="field-row">
<label for="entity-${index}-peak-direction">Direction</label>
<select id="entity-${index}-peak-direction" data-kind="entity-peak-direction" data-index="${index}" value="${this._getEffectiveMarkerDirection(scope, "peak")}">
<option value="inward"${this._getEffectiveMarkerDirection(scope, "peak") === "inward" ? " selected" : ""}>Inward</option>
<option value="outward"${this._getEffectiveMarkerDirection(scope, "peak") === "outward" ? " selected" : ""}>Outward</option>
</select>
</div>
${this._renderBuiltinMarkerLabelControls(scope, "peak", "Peak")}
`
});
const floorGroup = this._renderOverrideGroup({
index,
group: "floor",
title: "Floor",
summary: this._getFloorSummary(scope),
content: `
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-floor-inherit" type="checkbox" data-kind="entity-floor-inherit" data-index="${index}"${floorInherited ? " checked" : ""}>
<label for="entity-${index}-floor-inherit">Inherit card settings</label>
</div>
</div>
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-floor-enabled" type="checkbox" data-kind="entity-floor-enabled" data-index="${index}"${entityFloor.mode === "enabled" ? " checked" : ""}>
<label for="entity-${index}-floor-enabled">Floor enabled</label>
</div>
</div>
<div class="field-row">
<label for="entity-${index}-floor-color">Floor color</label>
${this._renderColorInput({
id: `entity-${index}-floor-color`,
kind: "entity-floor-color",
index,
value: entityFloor.color,
fallbackHex: "#888888",
placeholder: "inherit card default"
})}
</div>
<div class="field-row">
<label for="entity-${index}-floor-reset">Floor reset</label>
<select id="entity-${index}-floor-reset" data-kind="entity-floor-reset" data-index="${index}" value="${this._escapeAttribute(entityFloor.reset)}">
${this._renderResetOptions(entityFloor.reset)}
</select>
</div>
<div class="field-row">
<label for="entity-${index}-floor-direction">Direction</label>
<select id="entity-${index}-floor-direction" data-kind="entity-floor-direction" data-index="${index}" value="${this._getEffectiveMarkerDirection(scope, "floor")}">
<option value="inward"${this._getEffectiveMarkerDirection(scope, "floor") === "inward" ? " selected" : ""}>Inward</option>
<option value="outward"${this._getEffectiveMarkerDirection(scope, "floor") === "outward" ? " selected" : ""}>Outward</option>
</select>
</div>
${this._renderBuiltinMarkerLabelControls(scope, "floor", "Floor")}
`
});
const markersGroup = this._renderOverrideGroup({
index,
group: "markers",
title: "Reference markers",
summary: this._getGenericMarkersSummary(scope),
content: this._renderGenericMarkersEditor(scope)
});
const segmentsGroup = this._renderOverrideGroup({
index,
group: "segments",
title: "Segments",
summary: this._getSegmentsSummary(scope),
content: `
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-segments-inherit" type="checkbox" data-kind="entity-segments-inherit" data-index="${index}"${segmentsInherited ? " checked" : ""}>
<label for="entity-${index}-segments-inherit">Inherit card settings</label>
</div>
</div>
${this._isSegmentFillStyle(this._getEffectiveFillStyleValue(scope)) ? "" : '<div class="section-note">Only used with segment-based fill styles.</div>'}
${this._renderSegmentPreview(scope)}
<div class="field-row">
<label>Segments</label>
<div class="list">
${this._renderListRows(entitySegments, (segment, segmentIndex) => {
var _a4;
return `
<div class="segment-editor-row">
<div class="list-row triple segment-row">
<input type="text" data-kind="entity-segment-from" data-index="${index}" data-segment-index="${segmentIndex}" value="${this._escapeAttribute(this._getSegmentBoundaryText(scope, segmentIndex, "from", segment == null ? void 0 : segment.from))}" placeholder="0%">
<input type="text" data-kind="entity-segment-to" data-index="${index}" data-segment-index="${segmentIndex}" value="${this._escapeAttribute(this._getSegmentBoundaryText(scope, segmentIndex, "to", segment == null ? void 0 : segment.to))}" placeholder="100%">
<input type="color" data-kind="entity-segment-color" data-index="${index}" data-segment-index="${segmentIndex}" value="${this._escapeAttribute((_a4 = segment == null ? void 0 : segment.color) != null ? _a4 : "#4a9eff")}">
<button type="button" data-action="remove-entity-segment" data-index="${index}" data-segment-index="${segmentIndex}" aria-label="Remove" title="Remove">\u{1F5D1}</button>
</div>
<div id="entity-${index}-segment-row-hint-${segmentIndex}" class="section-note"${this._getSegmentRowValidationMessage(scope, segmentIndex) ? "" : ' style="display:none"'}>${this._escapeAttribute(this._getSegmentRowValidationMessage(scope, segmentIndex))}</div>
</div>
`;
})}
<div class="segment-draft">
<div class="list-row triple segment-row">
<input id="entity-${index}-segment-draft-from" type="text" data-kind="entity-segment-draft-from" data-index="${index}" value="${this._escapeAttribute(this._getSegmentDraftState(scope).from)}" placeholder="0%">
<input id="entity-${index}-segment-draft-to" type="text" data-kind="entity-segment-draft-to" data-index="${index}" value="${this._escapeAttribute(this._getSegmentDraftState(scope).to)}" placeholder="100%">
<input type="color" data-kind="entity-segment-draft-color" data-index="${index}" value="${this._escapeAttribute(this._getSegmentDraftState(scope).color || "#4a9eff")}">
<button type="button" data-action="add-entity-segment" data-index="${index}"${this._canAddSegment(scope) ? "" : " disabled"}>Add</button>
</div>
<div id="entity-${index}-segment-draft-hint" class="section-note"${this._getSegmentDraftValidationMessage(scope) ? "" : ' style="display:none"'}>${this._escapeAttribute(this._getSegmentDraftValidationMessage(scope))}</div>
</div>
</div>
</div>
`
});
const gradientStopsGroup = this._renderOverrideGroup({
index,
group: "gradient-stops",
title: "Gradient Stops",
summary: this._getGradientStopsSummary(scope),
content: `
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-gradient-stops-inherit" type="checkbox" data-kind="entity-gradient-stops-inherit" data-index="${index}"${gradientStopsInherited ? " checked" : ""}>
<label for="entity-${index}-gradient-stops-inherit">Inherit card settings</label>
</div>
</div>
${this._getEffectiveFillStyleValue(scope) !== "gradient" ? '<div class="section-note">Only used with Gradient fill style</div>' : ""}
${this._renderGradientPreview(scope, {
previewId: `entity-${index}-gradient-preview`,
trackId: `entity-${index}-gradient-preview-track`
})}
<div class="field-row">
<label>Gradient stops</label>
<div class="list gradient-stop-list">
${this._renderListRows(entityGradientStops, (stop, stopIndex) => {
var _a4, _b3;
return `
<div class="list-row gradient-stop-row">
<input type="number" min="0" max="100" step="any" data-kind="entity-gradient-pos" data-index="${index}" data-stop-index="${stopIndex}" value="${this._escapeAttribute(this._getGradientStopPosText(scope, stopIndex, (_a4 = stop == null ? void 0 : stop.pos) != null ? _a4 : ""))}" placeholder="0">
${this._renderColorInput({
id: `entity-${index}-gradient-color-${stopIndex}`,
kind: "entity-gradient-color",
index,
value: (_b3 = stop == null ? void 0 : stop.color) != null ? _b3 : "#4a9eff",
fallbackHex: "#4CAF50",
placeholder: "CSS color value",
extraDataset: { "stop-index": stopIndex }
})}
<button type="button" data-action="remove-entity-gradient-stop" data-index="${index}" data-stop-index="${stopIndex}" aria-label="Remove" title="Remove">\u{1F5D1}</button>
</div>
`;
})}
<div class="gradient-stop-draft">
<div class="list-row gradient-stop-row">
<input id="entity-${index}-gradient-draft-pos" type="number" min="0" max="100" step="any" data-kind="entity-gradient-draft-pos" data-index="${index}" value="${this._escapeAttribute(entityGradientDraft.pos)}" placeholder="0">
${this._renderColorInput({
id: `entity-${index}-gradient-draft-color`,
kind: "entity-gradient-draft-color",
index,
value: entityGradientDraft.color,
fallbackHex: "#4CAF50",
placeholder: "CSS color value"
})}
<button type="button" data-action="add-entity-gradient-stop" data-index="${index}"${this._canAddGradientStop(scope) ? "" : " disabled"}>Add</button>
</div>
${entityGradientDraftMessage ? `<div id="entity-${index}-gradient-draft-hint" class="section-note">${this._escapeAttribute(entityGradientDraftMessage)}</div>` : ""}
</div>
</div>
</div>
`
});
const baselineGroup = this._renderOverrideGroup({
index,
group: "baseline",
title: "Baseline",
summary: this._getBaselineOverrideSummary(scope),
content: `
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-baseline-inherit" type="checkbox" data-kind="entity-baseline-inherit" data-index="${index}"${baselineInherited ? " checked" : ""}>
<label for="entity-${index}-baseline-inherit">Inherit card settings</label>
</div>
</div>
<div class="field-row">
<button type="button" data-action="remove-baseline" data-scope-type="entity" data-index="${index}" aria-label="Remove Baseline" title="Remove Baseline">\u{1F5D1}</button>
</div>
<div class="field-row">
<label for="entity-${index}-baseline-mode">Baseline mode</label>
<select id="entity-${index}-baseline-mode" data-kind="entity-baseline-mode" data-index="${index}" value="${this._escapeAttribute(baselineMode2)}">
<option value="enabled"${baselineMode2 === "enabled" ? " selected" : ""}>enabled</option>
<option value="disabled"${baselineMode2 === "disabled" ? " selected" : ""}>disabled</option>
</select>
</div>
<div class="field-row">
<label for="entity-${index}-baseline-value">Baseline fallback</label>
<input id="entity-${index}-baseline-value" type="number" step="any" data-kind="entity-baseline-value" data-index="${index}" value="${this._escapeAttribute(baselineParts.fixed)}" placeholder="inherit card default">
</div>
<div class="field-row">
<label>Baseline entity</label>
${this._renderEntitySourceInput("entity-baseline-entity-source", index, baselineParts.entity, "inherit card default")}
</div>
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-baseline-above-color-enabled" type="checkbox" data-kind="entity-baseline-above-color-enabled" data-index="${index}"${this._isBaselineDirectionalColorEnabled(scope, "above") ? " checked" : ""}>
<label for="entity-${index}-baseline-above-color-enabled">Above-baseline color enabled</label>
</div>
</div>
<div class="field-row">
<label for="entity-${index}-baseline-above-color">Above-baseline color</label>
${this._renderColorInput({
id: `entity-${index}-baseline-above-color`,
kind: "entity-baseline-above-color",
index,
value: this._getEffectiveBaselineDirectionalColorValue(scope, "above"),
fallbackHex: "#000000",
placeholder: "inherit card default"
})}
</div>
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-baseline-below-color-enabled" type="checkbox" data-kind="entity-baseline-below-color-enabled" data-index="${index}"${this._isBaselineDirectionalColorEnabled(scope, "below") ? " checked" : ""}>
<label for="entity-${index}-baseline-below-color-enabled">Below-baseline color enabled</label>
</div>
</div>
<div class="field-row">
<label for="entity-${index}-baseline-below-color">Below-baseline color</label>
${this._renderColorInput({
id: `entity-${index}-baseline-below-color`,
kind: "entity-baseline-below-color",
index,
value: this._getEffectiveBaselineDirectionalColorValue(scope, "below"),
fallbackHex: "#000000",
placeholder: "inherit card default"
})}
</div>
`
});
const targetGroup = this._renderOverrideGroup({
index,
group: "target",
title: "Target",
summary: this._getTargetOverrideSummary(scope),
content: `
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-target-inherit" type="checkbox" data-kind="entity-target-inherit" data-index="${index}"${targetInherited ? " checked" : ""}>
<label for="entity-${index}-target-inherit">Inherit card settings</label>
</div>
</div>
<div class="field-row">
<label for="entity-${index}-target-mode">Target mode</label>
<select id="entity-${index}-target-mode" data-kind="entity-target-mode" data-index="${index}" value="${this._escapeAttribute(targetMode2)}">
<option value="enabled"${targetMode2 === "enabled" ? " selected" : ""}>enabled</option>
<option value="disabled"${targetMode2 === "disabled" ? " selected" : ""}>disabled</option>
</select>
</div>
<div class="field-row">
<label for="entity-${index}-target-value">Target fallback</label>
<input id="entity-${index}-target-value" type="number" step="any" data-kind="entity-target-value" data-index="${index}" value="${this._escapeAttribute(targetParts.fixed)}" placeholder="inherit card default">
</div>
<div class="field-row">
<label for="entity-${index}-target-shape">Target shape</label>
<select id="entity-${index}-target-shape" data-kind="entity-target-shape" data-index="${index}" value="${this._escapeAttribute(targetShape2)}">
<option value="diamond"${targetShape2 === "diamond" ? " selected" : ""}>diamond</option>
<option value="triangle"${targetShape2 === "triangle" ? " selected" : ""}>triangle</option>
</select>
</div>
<div class="field-row">
<label for="entity-${index}-target-direction">Direction</label>
<select id="entity-${index}-target-direction" data-kind="entity-target-direction" data-index="${index}" value="${targetDirection2}">
<option value="inward"${targetDirection2 === "inward" ? " selected" : ""}>Inward</option>
<option value="outward"${targetDirection2 === "outward" ? " selected" : ""}>Outward</option>
</select>
</div>
<div class="field-row">
<label>Target entity</label>
${this._renderEntitySourceInput("entity-target-entity-source", index, targetParts.entity, "inherit card default")}
</div>
<div class="field-row">
<label for="entity-${index}-target-color">Target color</label>
${this._renderColorInput({
id: `entity-${index}-target-color`,
kind: "entity-target-color",
index,
value: this._getEffectiveTargetColorValue(scope),
fallbackHex: "#888",
placeholder: "inherit card default"
})}
</div>
${this._renderBuiltinMarkerLabelControls(scope, "target", "Target")}
<div class="field-row">
<div class="toggle">
<input id="entity-${index}-target-above-fill-enabled" type="checkbox" data-kind="entity-target-above-fill-enabled" data-index="${index}"${this._isTargetAboveFillEnabled(scope) ? " checked" : ""}>
<label for="entity-${index}-target-above-fill-enabled">Above-target color enabled</label>
</div>
</div>
<div class="field-row">
<label for="entity-${index}-target-above-fill">Above-target color</label>
${this._renderColorInput({
id: `entity-${index}-target-above-fill`,
kind: "entity-target-above-fill-color",
index,
value: this._getEffectiveTargetAboveFillColorValue(scope),
fallbackHex: "#000000",
placeholder: "inherit card default"
})}
</div>
`
});
return `
${scaleGroup}
${targetGroup}
${peakGroup}
${floorGroup}
${markersGroup}
${barGroup}
${baselineGroup}
${needleGroup}
${segmentsGroup}
${gradientStopsGroup}
${layoutGroup}
${formattingGroup}
`;
})()}
</div>
</div>
`;
})}
<button type="button" data-action="add-entity">Add entity</button>
</div>
</div>
</div>
<div class="section">
<div class="section-head">
<h3>Scale</h3>
<div class="section-note">Entity values take precedence. Fixed values are used as fallback.</div>
</div>
<div class="inline-row editor-grid">
<div class="field-row">
<label for="scale-min">Min fallback</label>
<input id="scale-min" type="number" step="any" data-field="scale-min" value="${this._escapeAttribute(scaleMin)}">
</div>
<div class="field-row">
<label>Min entity</label>
${this._renderEntitySourceInput("scale-min-entity-source", "card", scaleMinEntity)}
</div>
<div class="field-row">
<label for="scale-max">Max fallback</label>
<input id="scale-max" type="number" step="any" data-field="scale-max" value="${this._escapeAttribute(scaleMax)}">
</div>
<div class="field-row">
<label>Max entity</label>
${this._renderEntitySourceInput("scale-max-entity-source", "card", scaleMaxEntity)}
</div>
</div>
</div>
<div class="section">
<div class="section-head">
<h3>Markers</h3>
<div class="section-note">Configure Target, Peak, Floor, and custom reference markers.</div>
</div>
<div class="field-grid">
${this._renderCardGroup({
group: "marker-target",
title: "Target",
summary: this._getCardTargetMarkerSummary(),
content: `
<div class="field-grid">
<div class="field-row">
<label for="target-mode">Target mode</label>
<select id="target-mode" data-field="target-mode" value="${this._escapeAttribute(targetMode)}">
<option value="auto"${targetMode === "auto" ? " selected" : ""}>auto</option>
<option value="enabled"${targetMode === "enabled" ? " selected" : ""}>enabled</option>
<option value="disabled"${targetMode === "disabled" ? " selected" : ""}>disabled</option>
</select>
</div>
<div class="field-row">
<label for="target-value">Target fallback</label>
<input id="target-value" type="number" step="any" data-field="target-value" value="${this._escapeAttribute(target.fixed)}">
</div>
<div class="field-row">
<label for="target-shape">Target shape</label>
<select id="target-shape" data-field="target-shape" value="${this._escapeAttribute(targetShape)}">
<option value="diamond"${targetShape === "diamond" ? " selected" : ""}>diamond</option>
<option value="triangle"${targetShape === "triangle" ? " selected" : ""}>triangle</option>
</select>
</div>
<div class="field-row">
<label for="target-direction">Direction</label>
<select id="target-direction" data-field="target-direction" value="${targetDirection}">
<option value="inward"${targetDirection === "inward" ? " selected" : ""}>Inward</option>
<option value="outward"${targetDirection === "outward" ? " selected" : ""}>Outward</option>
</select>
</div>
<div class="field-row">
<label>Target entity</label>
${this._renderEntitySourceInput("target-entity-source", "card", target.entity)}
</div>
<div class="field-row">
<label for="target-color">Target color</label>
${this._renderColorInput({
id: "target-color",
field: "target-color",
value: targetColor,
fallbackHex: "#888",
placeholder: "#888"
})}
</div>
${this._renderBuiltinMarkerLabelControls({ type: "card" }, "target", "Target")}
<div class="field-row">
<div class="toggle">
<input id="target-above-fill-enabled" type="checkbox" data-field="target-above-fill-enabled"${this._isTargetAboveFillEnabled({ type: "card" }) ? " checked" : ""}>
<label for="target-above-fill-enabled">Above-target color enabled</label>
</div>
</div>
<div class="field-row">
<label for="target-above-fill-color">Above-target color</label>
${this._renderColorInput({
id: "target-above-fill-color",
field: "target-above-fill-color",
value: targetAboveFillColor,
fallbackHex: "#000000"
})}
</div>
</div>`
})}
${this._renderCardGroup({
group: "marker-peak",
title: "Peak",
summary: this._getMarkerResetSummary("peak"),
content: `
<div class="field-grid">
<div class="field-row">
<div class="toggle">
<input id="peak-show" type="checkbox" data-field="peak-show"${cardPeak.mode === "enabled" ? " checked" : ""}>
<label for="peak-show">Peak enabled</label>
</div>
</div>
<div class="field-row">
<label for="peak-color">Peak color</label>
${this._renderColorInput({
id: "peak-color",
field: "peak-color",
value: cardPeak.color,
fallbackHex: "#888",
placeholder: "#888"
})}
</div>
<div class="field-row">
<label for="peak-reset">Peak reset</label>
<select id="peak-reset" data-field="peak-reset" value="${this._escapeAttribute(cardPeakExtras.reset)}">
${this._renderResetOptions(cardPeakExtras.reset)}
</select>
</div>
<div class="field-row">
<label for="peak-direction">Direction</label>
<select id="peak-direction" data-field="peak-direction" value="${this._getEffectiveMarkerDirection({ type: "card" }, "peak")}">
<option value="inward"${this._getEffectiveMarkerDirection({ type: "card" }, "peak") === "inward" ? " selected" : ""}>Inward</option>
<option value="outward"${this._getEffectiveMarkerDirection({ type: "card" }, "peak") === "outward" ? " selected" : ""}>Outward</option>
</select>
</div>
${this._renderBuiltinMarkerLabelControls({ type: "card" }, "peak", "Peak")}
</div>`
})}
${this._renderCardGroup({
group: "marker-floor",
title: "Floor",
summary: this._getMarkerResetSummary("floor"),
content: `
<div class="field-grid">
<div class="field-row">
<div class="toggle">
<input id="floor-show" type="checkbox" data-field="floor-show"${cardFloor.mode === "enabled" ? " checked" : ""}>
<label for="floor-show">Floor enabled</label>
</div>
</div>
<div class="field-row">
<label for="floor-color">Floor color</label>
${this._renderColorInput({
id: "floor-color",
field: "floor-color",
value: cardFloor.color,
fallbackHex: "#888888",
placeholder: "#888888"
})}
</div>
<div class="field-row">
<label for="floor-reset">Floor reset</label>
<select id="floor-reset" data-field="floor-reset" value="${this._escapeAttribute(cardFloor.reset)}">
${this._renderResetOptions(cardFloor.reset)}
</select>
</div>
<div class="field-row">
<label for="floor-direction">Direction</label>
<select id="floor-direction" data-field="floor-direction" value="${this._getEffectiveMarkerDirection({ type: "card" }, "floor")}">
<option value="inward"${this._getEffectiveMarkerDirection({ type: "card" }, "floor") === "inward" ? " selected" : ""}>Inward</option>
<option value="outward"${this._getEffectiveMarkerDirection({ type: "card" }, "floor") === "outward" ? " selected" : ""}>Outward</option>
</select>
</div>
${this._renderBuiltinMarkerLabelControls({ type: "card" }, "floor", "Floor")}
</div>`
})}
${this._renderCardGroup({
group: "generic-markers",
title: "Generic Reference Markers",
summary: this._getGenericMarkersSummary({ type: "card" }),
content: this._renderGenericMarkersEditor({ type: "card" })
})}
</div>
</div>
<div class="section">
<div class="section-head">
<h3>Bar Appearance</h3>
<div class="section-note">Choose the bar rendering mode and base bar colors.</div>
</div>
<div class="inline-row editor-grid">
<div class="field-row">
<label for="bar-fill-style">Fill style</label>
<select id="bar-fill-style" data-field="bar-fill-style" value="${this._escapeAttribute(fillStyle)}">
<option value="solid"${fillStyle === "solid" ? " selected" : ""}>solid</option>
<option value="gradient"${fillStyle === "gradient" ? " selected" : ""}>gradient</option>
<option value="bands"${fillStyle === "bands" ? " selected" : ""}>bands</option>
<option value="band_gradient"${fillStyle === "band_gradient" ? " selected" : ""}>band_gradient</option>
<option value="soft_bands"${fillStyle === "soft_bands" ? " selected" : ""}>soft_bands</option>
</select>
</div>
<div class="field-row">
<div class="toggle">
<input id="bar-solid-fill" type="checkbox" data-field="bar-solid-fill"${barSolidFill ? " checked" : ""}>
<label for="bar-solid-fill">Solid fill</label>
</div>
</div>
<div class="field-row">
<label for="bar-color">Bar color</label>
${this._renderColorInput({
id: "bar-color",
field: "bar-color",
value: barColor,
fallbackHex: "#4a9eff",
placeholder: "#4a9eff"
})}
</div>
</div>
${this._renderCardGroup({
group: "baseline",
title: "Baseline",
summary: this._getCardBaselineSummary(),
content: `
<div class="field-grid">
<div class="field-row">
<button type="button" data-action="remove-baseline" data-scope-type="card" aria-label="Remove Baseline" title="Remove Baseline">\u{1F5D1}</button>
</div>
<div class="field-row">
<label for="baseline-mode">Baseline mode</label>
<select id="baseline-mode" data-field="baseline-mode" value="${this._escapeAttribute(baselineMode)}">
<option value="auto"${baselineMode === "auto" ? " selected" : ""}>auto</option>
<option value="enabled"${baselineMode === "enabled" ? " selected" : ""}>enabled</option>
<option value="disabled"${baselineMode === "disabled" ? " selected" : ""}>disabled</option>
</select>
</div>
<div class="field-row">
<div class="section-note">Auto shows the baseline when a baseline value is configured.</div>
</div>
<div class="field-row">
<label for="baseline-value">Baseline fallback</label>
<input id="baseline-value" type="number" step="any" data-field="baseline-value" value="${this._escapeAttribute(baseline.fixed)}">
</div>
<div class="field-row">
<label>Baseline entity</label>
${this._renderEntitySourceInput("baseline-entity-source", "card", baseline.entity)}
</div>
<div class="field-row">
<div class="toggle">
<input id="baseline-above-color-enabled" type="checkbox" data-field="baseline-above-color-enabled"${this._isBaselineDirectionalColorEnabled({ type: "card" }, "above") ? " checked" : ""}>
<label for="baseline-above-color-enabled">Above-baseline color enabled</label>
</div>
</div>
<div class="field-row">
<label for="baseline-above-color">Above-baseline color</label>
${this._renderColorInput({
id: "baseline-above-color",
field: "baseline-above-color",
value: baselineAboveColor,
fallbackHex: "#000000"
})}
</div>
<div class="field-row">
<div class="toggle">
<input id="baseline-below-color-enabled" type="checkbox" data-field="baseline-below-color-enabled"${this._isBaselineDirectionalColorEnabled({ type: "card" }, "below") ? " checked" : ""}>
<label for="baseline-below-color-enabled">Below-baseline color enabled</label>
</div>
</div>
<div class="field-row">
<label for="baseline-below-color">Below-baseline color</label>
${this._renderColorInput({
id: "baseline-below-color",
field: "baseline-below-color",
value: baselineBelowColor,
fallbackHex: "#000000"
})}
</div>
</div>`
})}
<div class="inline-row editor-grid">
<div class="field-row">
<label for="bar-needle-mode">Needle enabled</label>
<select id="bar-needle-mode" data-field="bar-needle-mode" value="${this._escapeAttribute(cardNeedle.mode)}">
<option value="disabled"${cardNeedle.mode === "disabled" ? " selected" : ""}>disabled</option>
<option value="enabled"${cardNeedle.mode === "enabled" ? " selected" : ""}>enabled</option>
</select>
</div>
<div class="field-row">
<label for="bar-needle-color">Needle color</label>
${this._renderColorInput({
id: "bar-needle-color",
field: "bar-needle-color",
value: cardNeedle.color,
fallbackHex: "#ffffff",
placeholder: "#ffffff"
})}
</div>
</div>
</div>
<div class="section">
<div class="section-head">
<h3>Segments</h3>
<div class="section-note">Segments define colored value ranges.</div>
</div>
${this._renderCardGroup({
group: "segments",
title: "Segments",
summary: this._getSegmentsSummary({ type: "card" }),
inactive: !this._isSegmentFillStyle(fillStyle),
content: `
${this._isSegmentFillStyle(fillStyle) ? "" : '<div class="section-note">Only used with segment-based fill styles.</div>'}
${this._renderSegmentPreview({ type: "card" })}
<div class="field-row">
<label>Segments</label>
<div class="list">
${defaultSegmentsVisible ? '<div class="section-note">Default bands</div>' : ""}
${this._renderListRows(segments, (segment, index) => {
var _a2;
return `
<div class="segment-editor-row">
<div class="list-row triple segment-row">
<input type="text" data-kind="segment-from" data-index="${index}" value="${this._escapeAttribute(this._getSegmentBoundaryText({ type: "card" }, index, "from", segment == null ? void 0 : segment.from))}" placeholder="0%">
<input type="text" data-kind="segment-to" data-index="${index}" value="${this._escapeAttribute(this._getSegmentBoundaryText({ type: "card" }, index, "to", segment == null ? void 0 : segment.to))}" placeholder="100%">
<input type="color" data-kind="segment-color" data-index="${index}" value="${this._escapeAttribute((_a2 = segment == null ? void 0 : segment.color) != null ? _a2 : "#4a9eff")}">
<button type="button" data-action="remove-segment" data-index="${index}" aria-label="Remove" title="Remove">\u{1F5D1}</button>
</div>
<div id="segment-row-hint-${index}" class="section-note"${this._getSegmentRowValidationMessage({ type: "card" }, index) ? "" : ' style="display:none"'}>${this._escapeAttribute(this._getSegmentRowValidationMessage({ type: "card" }, index))}</div>
</div>
`;
})}
<div class="segment-draft">
<div class="list-row triple segment-row">
<input id="segment-draft-from" type="text" data-kind="segment-draft-from" value="${this._escapeAttribute(this._getSegmentDraftState({ type: "card" }).from)}" placeholder="0%">
<input id="segment-draft-to" type="text" data-kind="segment-draft-to" value="${this._escapeAttribute(this._getSegmentDraftState({ type: "card" }).to)}" placeholder="100%">
<input type="color" data-kind="segment-draft-color" value="${this._escapeAttribute(this._getSegmentDraftState({ type: "card" }).color || "#4a9eff")}">
<button type="button" data-action="add-segment"${this._canAddSegment({ type: "card" }) ? "" : " disabled"}>Add</button>
</div>
<div id="segment-draft-hint" class="section-note"${this._getSegmentDraftValidationMessage({ type: "card" }) ? "" : ' style="display:none"'}>${this._escapeAttribute(this._getSegmentDraftValidationMessage({ type: "card" }))}</div>
</div>
</div>
</div>
`
})}
</div>
<div class="section">
<div class="section-head">
<h3>Gradient Stops</h3>
<div class="section-note">Gradient stops define a smooth color transition from 0 to 100%.</div>
</div>
${this._renderCardGroup({
group: "gradient-stops",
title: "Gradient Stops",
summary: gradientStopsSummary,
inactive: gradientStopsInactive,
content: `
${gradientStopsInactive ? '<div class="section-note">Only used with Gradient fill style</div>' : ""}
${this._renderGradientPreview({ type: "card" }, {
previewId: "card-gradient-preview",
trackId: "card-gradient-preview-track"
})}
<div class="field-row">
<label>Gradient stops</label>
<div class="list gradient-stop-list">
${this._renderListRows(gradientStops, (stop, index) => {
var _a2, _b;
return `
<div class="list-row gradient-stop-row">
<input type="number" min="0" max="100" step="any" data-kind="gradient-pos" data-index="${index}" value="${this._escapeAttribute(this._getGradientStopPosText({ type: "card" }, index, (_a2 = stop == null ? void 0 : stop.pos) != null ? _a2 : ""))}" placeholder="0">
${this._renderColorInput({
id: `gradient-color-${index}`,
kind: "gradient-color",
index,
value: (_b = stop == null ? void 0 : stop.color) != null ? _b : "#4a9eff",
fallbackHex: "#4CAF50",
placeholder: "CSS color value"
})}
<button type="button" data-action="remove-gradient-stop" data-index="${index}" aria-label="Remove" title="Remove">\u{1F5D1}</button>
</div>
`;
})}
<div class="gradient-stop-draft">
<div class="list-row gradient-stop-row">
<input id="gradient-draft-pos" type="number" min="0" max="100" step="any" data-kind="gradient-draft-pos" value="${this._escapeAttribute(gradientDraft.pos)}" placeholder="0">
${this._renderColorInput({
id: "gradient-draft-color",
kind: "gradient-draft-color",
index: "card",
value: gradientDraft.color,
fallbackHex: "#4CAF50",
placeholder: "CSS color value"
})}
<button type="button" data-action="add-gradient-stop"${this._canAddGradientStop({ type: "card" }) ? "" : " disabled"}>Add</button>
</div>
${gradientDraftMessage ? `<div id="gradient-draft-hint" class="section-note">${this._escapeAttribute(gradientDraftMessage)}</div>` : ""}
</div>
</div>
</div>
`
})}
</div>
<div class="section">
<div class="section-head">
<h3>Layout</h3>
</div>
<div class="inline-row editor-grid">
<div class="field-row">
<label for="layout-height">Row height</label>
<input id="layout-height" type="number" min="24" step="1" data-field="layout-height" value="${this._escapeAttribute(layoutHeight)}">
</div>
<div class="field-row">
<label for="layout-label-position">Label position</label>
<select id="layout-label-position" data-field="layout-label-position" value="${this._escapeAttribute(layoutLabelPosition)}">
<option value="left"${layoutLabelPosition === "left" ? " selected" : ""}>left</option>
<option value="above"${layoutLabelPosition === "above" ? " selected" : ""}>above</option>
<option value="inside"${layoutLabelPosition === "inside" ? " selected" : ""}>inside</option>
<option value="hero"${layoutLabelPosition === "hero" ? " selected" : ""}>hero</option>
<option value="off"${layoutLabelPosition === "off" ? " selected" : ""}>off</option>
</select>
</div>
${layoutLabelPosition === "hero" ? `
<div class="field-row">
<label for="layout-label-hero-size">Hero size</label>
<select id="layout-label-hero-size" data-field="layout-label-hero-size" value="${this._escapeAttribute(layoutHeroSize)}">
<option value="small"${layoutHeroSize === "small" ? " selected" : ""}>small</option>
<option value="medium"${layoutHeroSize === "medium" ? " selected" : ""}>medium</option>
<option value="large"${layoutHeroSize === "large" ? " selected" : ""}>large</option>
</select>
</div>
<div class="field-row">
<label for="layout-hero-value-size">Maximum font size</label>
<input id="layout-hero-value-size" type="number" min="12" max="112" step="1" data-field="layout-hero-value-size" value="${this._escapeAttribute(layoutHeroValueSize)}" placeholder="use Hero size preset">
<div class="section-note">The hero value may render smaller when needed to fit. A custom value overrides the Hero size preset.</div>
</div>
` : ""}
<div class="field-row">
<label for="layout-label-width">Label width</label>
<input id="layout-label-width" type="number" step="1" data-field="layout-label-width" value="${this._escapeAttribute(layoutLabelWidth)}">
</div>
</div>
</div>
<div class="section">
<div class="section-head">
<h3>Formatting</h3>
</div>
<div class="inline-row editor-grid">
<div class="field-row">
<label for="formatting-unit">Unit</label>
<input id="formatting-unit" type="text" data-field="formatting-unit" value="${this._escapeAttribute(formattingUnit)}">
</div>
<div class="field-row">
<label for="formatting-decimal">Decimals</label>
<input id="formatting-decimal" type="number" min="0" step="1" data-field="formatting-decimal" value="${this._escapeAttribute(formattingDecimal)}">
</div>
</div>
</div>
</div>
`;
this._bindShadowListeners();
this._syncEntityPickers();
this._lastRenderedConfigJson = this._serializeConfig(this._draftConfig);
this._applyPendingFocus();
} finally {
this._isRendering = false;
}
}
_bindShadowListeners() {
if (!this.shadowRoot || this._shadowListenersAttached) return;
this.shadowRoot.addEventListener("click", this._boundHandleClick);
this.shadowRoot.addEventListener("change", this._boundHandleChange);
this.shadowRoot.addEventListener("input", this._boundHandleInput);
this.shadowRoot.addEventListener("value-changed", this._boundHandleValueChanged);
this.shadowRoot.addEventListener("keydown", this._boundHandleKeydown);
this._shadowListenersAttached = true;
}
_syncEntityPickers() {
if (!this.shadowRoot) return;
const entities = this._getEntitiesValue();
const syncPicker = (picker) => {
var _a, _b, _c, _d;
const kind = picker.dataset.kind;
const indexValue = picker.dataset.index;
const index = Number(indexValue);
picker.hass = this._hass;
picker.allowCustomEntity = true;
if (kind === "entity-picker") {
const entry = entities[index];
picker.value = (_a = entry == null ? void 0 : entry.entity) != null ? _a : "";
picker.label = `Entity ${index + 1}`;
return;
}
if (kind === "scale-min-entity-source") {
picker.value = this._getScaleEntityValue("min");
picker.label = "Min entity";
return;
}
if (kind === "scale-max-entity-source") {
picker.value = this._getScaleEntityValue("max");
picker.label = "Max entity";
return;
}
if (kind === "baseline-entity-source") {
picker.value = this._getBaselineResolvableValue({ type: "card" }).entity;
picker.label = "Baseline entity";
return;
}
if (kind === "target-entity-source") {
picker.value = this._getTargetResolvableValue({ type: "card" }).entity;
picker.label = "Target entity";
return;
}
if (kind === "entity-override-min-entity-source") {
picker.value = this._getEffectiveResolvableScopedValue({ type: "entity", index }, "min").entity;
picker.label = `Entity ${index + 1} min entity`;
return;
}
if (kind === "entity-override-max-entity-source") {
picker.value = this._getEffectiveResolvableScopedValue({ type: "entity", index }, "max").entity;
picker.label = `Entity ${index + 1} max entity`;
return;
}
if (kind === "entity-baseline-entity-source") {
picker.value = this._getEffectiveBaselineResolvableValue({ type: "entity", index }).entity;
picker.label = `Entity ${index + 1} baseline entity`;
return;
}
if (kind === "entity-target-entity-source") {
picker.value = this._getEffectiveTargetResolvableValue({ type: "entity", index }).entity;
picker.label = `Entity ${index + 1} target entity`;
return;
}
if (kind === "generic-marker-entity") {
const scope = this._getGenericMarkerScope(picker);
const marker = this._getGenericMarkers(scope)[Number(picker.dataset.markerIndex)];
picker.value = (_b = this._getGenericMarkerSource(marker).entity) != null ? _b : "";
picker.label = "Reference marker entity";
}
if (kind === "generic-marker-label-entity") {
const scope = this._getGenericMarkerScope(picker);
const marker = this._getGenericMarkers(scope)[Number(picker.dataset.markerIndex)];
picker.value = (_d = (_c = marker == null ? void 0 : marker.label) == null ? void 0 : _c.entity) != null ? _d : "";
picker.label = "Label content entity";
}
};
[
'ha-entity-picker[data-kind="entity-picker"]',
'ha-entity-picker[data-kind="scale-min-entity-source"]',
'ha-entity-picker[data-kind="scale-max-entity-source"]',
'ha-entity-picker[data-kind="baseline-entity-source"]',
'ha-entity-picker[data-kind="target-entity-source"]',
'ha-entity-picker[data-kind="entity-override-min-entity-source"]',
'ha-entity-picker[data-kind="entity-override-max-entity-source"]',
'ha-entity-picker[data-kind="entity-baseline-entity-source"]',
'ha-entity-picker[data-kind="entity-target-entity-source"]',
'ha-entity-picker[data-kind="generic-marker-entity"]',
'ha-entity-picker[data-kind="generic-marker-label-entity"]'
].forEach((selector) => {
this.shadowRoot.querySelectorAll(selector).forEach(syncPicker);
});
if (customElements.whenDefined) {
customElements.whenDefined("ha-entity-picker").then(() => {
[
'ha-entity-picker[data-kind="entity-picker"]',
'ha-entity-picker[data-kind="scale-min-entity-source"]',
'ha-entity-picker[data-kind="scale-max-entity-source"]',
'ha-entity-picker[data-kind="baseline-entity-source"]',
'ha-entity-picker[data-kind="target-entity-source"]',
'ha-entity-picker[data-kind="entity-override-min-entity-source"]',
'ha-entity-picker[data-kind="entity-override-max-entity-source"]',
'ha-entity-picker[data-kind="entity-baseline-entity-source"]',
'ha-entity-picker[data-kind="entity-target-entity-source"]',
'ha-entity-picker[data-kind="generic-marker-entity"]',
'ha-entity-picker[data-kind="generic-marker-label-entity"]'
].forEach((selector) => {
var _a;
(_a = this.shadowRoot) == null ? void 0 : _a.querySelectorAll(selector).forEach(syncPicker);
});
}).catch(() => {
});
}
}
_handleClick(event) {
var _a, _b, _c, _d, _e, _f;
const target = (_c = (_b = (_a = event.target) == null ? void 0 : _a.closest) == null ? void 0 : _b.call(_a, "[data-action]")) != null ? _c : event.target;
const action = (_d = target == null ? void 0 : target.dataset) == null ? void 0 : _d.action;
if (!action) return;
if (target == null ? void 0 : target.disabled) return;
if (action === "add-entity") {
const nextEntities = [...this._getEntitiesValue(), { entity: "" }];
const nextEntries = this._buildEntityConfigEntries(nextEntities);
this._queuePostRenderFocus(`[data-kind="entity-picker"][data-index="${nextEntities.length - 1}"], [data-kind="entity-input"][data-index="${nextEntities.length - 1}"]`);
if (Array.isArray(this._draftConfig.entities) || nextEntries.length > 1 || !this._draftConfig.entity) {
let nextConfig = this._setPathValue(this._draftConfig, ["entities"], nextEntries);
if (!Array.isArray(this._draftConfig.entities) && this._draftConfig.entity !== void 0) {
nextConfig = this._deletePathValue(nextConfig, ["entity"]);
if (this._draftConfig.name !== void 0) {
nextConfig = this._deletePathValue(nextConfig, ["name"]);
}
if (this._draftConfig.icon !== void 0) {
nextConfig = this._deletePathValue(nextConfig, ["icon"]);
}
}
this._applyUserConfig(nextConfig, { rerender: true });
} else {
this._setValueAtPath(["entity"], (_f = (_e = nextEntities[0]) == null ? void 0 : _e.entity) != null ? _f : "", { rerender: true });
}
return;
}
if (action === "move-entity-up") {
this._moveEntityRow(Number(target.dataset.index), -1);
return;
}
if (action === "move-entity-down") {
this._moveEntityRow(Number(target.dataset.index), 1);
return;
}
if (action === "duplicate-entity") {
const sourceIndex = Number(target.dataset.index);
this._queuePostRenderFocus(`[data-kind="entity-name"][data-index="${sourceIndex + 1}"], [data-kind="entity-picker"][data-index="${sourceIndex + 1}"], [data-kind="entity-input"][data-index="${sourceIndex + 1}"]`);
this._duplicateEntityRow(sourceIndex);
return;
}
if (action === "toggle-entity-overrides") {
this._toggleEntityOverrideExpanded(Number(target.dataset.index));
return;
}
if (action === "toggle-override-group") {
this._toggleOverrideGroupExpanded(Number(target.dataset.index), target.dataset.group);
return;
}
if (action === "toggle-card-group") {
this._toggleCardGroupExpanded(target.dataset.group);
return;
}
if (action === "toggle-generic-marker") {
this._toggleGenericMarkerExpanded(target.dataset.markerUiId);
return;
}
if (action === "remove-entity") {
this._removeEntityRow(Number(target.dataset.index));
return;
}
if (action === "remove-baseline") {
const scope = target.dataset.scopeType === "entity" ? { type: "entity", index: Number(target.dataset.index) } : { type: "card" };
this._removeBaseline(scope);
return;
}
if (action === "add-generic-marker") {
const scope = this._getGenericMarkerScope(target);
const markers = this._getGenericMarkers(scope);
const markerUiIds = this._getGenericMarkerUiIds(scope, markers.length);
markers.push({ at: { fixed: 50 } });
const markerUiId = `marker-${++this._nextGenericMarkerUiId}`;
markerUiIds.push(markerUiId);
this._expandedGenericMarkerUiIds.add(markerUiId);
this._setGenericMarkerList(scope, markers, { rerender: true });
return;
}
if (action === "remove-generic-marker" || action === "move-generic-marker-up" || action === "move-generic-marker-down") {
const scope = this._getGenericMarkerScope(target);
const markerIndex = Number(target.dataset.markerIndex);
const markers = this._getGenericMarkers(scope);
const markerUiIds = this._getGenericMarkerUiIds(scope, markers.length);
if (action === "remove-generic-marker") {
markers.splice(markerIndex, 1);
this._expandedGenericMarkerUiIds.delete(markerUiIds[markerIndex]);
markerUiIds.splice(markerIndex, 1);
} else {
const nextIndex = markerIndex + (action === "move-generic-marker-up" ? -1 : 1);
if (nextIndex < 0 || nextIndex >= markers.length) return;
[markers[markerIndex], markers[nextIndex]] = [markers[nextIndex], markers[markerIndex]];
[markerUiIds[markerIndex], markerUiIds[nextIndex]] = [markerUiIds[nextIndex], markerUiIds[markerIndex]];
}
this._setGenericMarkerList(scope, markers, { rerender: true });
return;
}
if (action === "add-gradient-stop") {
this._queuePostRenderFocus("#gradient-draft-pos");
this._commitGradientStopDraft({ type: "card" });
return;
}
if (action === "remove-gradient-stop") {
const index = Number(target.dataset.index);
this._setGradientStops(this._getGradientStopsValue().filter((_, stopIndex) => stopIndex !== index), { rerender: true });
return;
}
if (action === "add-entity-gradient-stop") {
const entityIndex = Number(target.dataset.index);
this._queuePostRenderFocus(`#entity-${entityIndex}-gradient-draft-pos`);
this._commitGradientStopDraft({ type: "entity", index: entityIndex });
return;
}
if (action === "remove-entity-gradient-stop") {
const scope = { type: "entity", index: Number(target.dataset.index) };
const stopIndex = Number(target.dataset.stopIndex);
this._setScopedGradientStops(scope, this._getScopedGradientStopsValue(scope).filter((_, index) => index !== stopIndex), { rerender: true });
return;
}
if (action === "add-segment") {
this._queuePostRenderFocus("#segment-draft-from");
this._commitSegmentDraft({ type: "card" });
return;
}
if (action === "remove-segment") {
const index = Number(target.dataset.index);
this._setSegments(this._getSegmentsValue().filter((_, segmentIndex) => segmentIndex !== index), { rerender: true, sort: true });
return;
}
if (action === "add-entity-segment") {
const entityIndex = Number(target.dataset.index);
this._queuePostRenderFocus(`#entity-${entityIndex}-segment-draft-from`);
this._commitSegmentDraft({ type: "entity", index: entityIndex });
return;
}
if (action === "remove-entity-segment") {
const scope = { type: "entity", index: Number(target.dataset.index) };
const segmentIndex = Number(target.dataset.segmentIndex);
this._setScopedSegments(scope, this._getScopedSegmentsValue(scope).filter((_, index) => index !== segmentIndex), { rerender: true, sort: true });
return;
}
}
_handleChange(event) {
var _a, _b;
const kind = (_b = (_a = event.target) == null ? void 0 : _a.dataset) == null ? void 0 : _b.kind;
if (kind === "gradient-pos") {
const stopIndex = Number(event.target.dataset.index);
this._commitGradientStopPosEdit({ type: "card" }, stopIndex, event.target.value, event.target);
return;
}
if (kind === "entity-gradient-pos") {
const scope = { type: "entity", index: Number(event.target.dataset.index) };
const stopIndex = Number(event.target.dataset.stopIndex);
this._commitGradientStopPosEdit(scope, stopIndex, event.target.value, event.target);
return;
}
if (kind === "segment-from" || kind === "segment-to") {
this._commitSegmentBoundaryEdit({ type: "card" }, Number(event.target.dataset.index), kind === "segment-from" ? "from" : "to", event.target.value, event.target);
return;
}
if (kind === "entity-segment-from" || kind === "entity-segment-to") {
this._commitSegmentBoundaryEdit({ type: "entity", index: Number(event.target.dataset.index) }, Number(event.target.dataset.segmentIndex), kind === "entity-segment-from" ? "from" : "to", event.target.value, event.target);
return;
}
this._handleFieldEvent(event);
}
_handleInput(event) {
var _a;
const target = event.target;
if (!target) return;
if (target.tagName === "HA-ENTITY-PICKER") return;
if (target.tagName === "INPUT" && target.type === "checkbox") return;
const kind = (_a = target.dataset) == null ? void 0 : _a.kind;
if (kind === "gradient-pos") {
this._setGradientStopPosText({ type: "card" }, Number(target.dataset.index), target.value);
return;
}
if (kind === "entity-gradient-pos") {
this._setGradientStopPosText({ type: "entity", index: Number(target.dataset.index) }, Number(target.dataset.stopIndex), target.value);
return;
}
if (kind === "segment-from" || kind === "segment-to") {
this._setSegmentBoundaryText({ type: "card" }, Number(target.dataset.index), kind === "segment-from" ? "from" : "to", target.value);
this._refreshSegmentUi({ type: "card" });
return;
}
if (kind === "entity-segment-from" || kind === "entity-segment-to") {
const scope = { type: "entity", index: Number(target.dataset.index) };
this._setSegmentBoundaryText(scope, Number(target.dataset.segmentIndex), kind === "entity-segment-from" ? "from" : "to", target.value);
this._refreshSegmentUi(scope);
return;
}
this._handleFieldEvent(event);
}
_handleValueChanged(event) {
var _a;
if (((_a = event.target) == null ? void 0 : _a.tagName) === "HA-ENTITY-PICKER") {
this._handleFieldEvent(event);
}
}
_handleKeydown(event) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
const kind = (_b = (_a = event.target) == null ? void 0 : _a.dataset) == null ? void 0 : _b.kind;
if (event.key === "Escape") {
if (kind === "gradient-draft-pos" || kind === "gradient-draft-color") {
(_c = event.preventDefault) == null ? void 0 : _c.call(event);
this._gradientStopsDrafts.set(this._getGradientStopsDraftKey({ type: "card" }), this._createGradientStopDraftState({ type: "card" }));
this._render();
return;
}
if (kind === "entity-gradient-draft-pos" || kind === "entity-gradient-draft-color") {
(_d = event.preventDefault) == null ? void 0 : _d.call(event);
const scope = { type: "entity", index: Number(event.target.dataset.index) };
this._gradientStopsDrafts.set(this._getGradientStopsDraftKey(scope), this._createGradientStopDraftState(scope));
this._render();
}
if (kind === "segment-draft-from" || kind === "segment-draft-to" || kind === "segment-draft-color") {
(_e = event.preventDefault) == null ? void 0 : _e.call(event);
this._segmentDrafts.set(this._getSegmentsScopeKey({ type: "card" }), this._createSegmentDraftState({ type: "card" }));
this._render();
return;
}
if (kind === "entity-segment-draft-from" || kind === "entity-segment-draft-to" || kind === "entity-segment-draft-color") {
(_f = event.preventDefault) == null ? void 0 : _f.call(event);
const scope = { type: "entity", index: Number(event.target.dataset.index) };
this._segmentDrafts.set(this._getSegmentsScopeKey(scope), this._createSegmentDraftState(scope));
this._render();
return;
}
return;
}
if (event.key !== "Enter") {
return;
}
if (kind === "gradient-pos") {
(_g = event.preventDefault) == null ? void 0 : _g.call(event);
const stopIndex = Number(event.target.dataset.index);
this._commitGradientStopPosEdit({ type: "card" }, stopIndex, event.target.value, event.target);
return;
}
if (kind === "entity-gradient-pos") {
(_h = event.preventDefault) == null ? void 0 : _h.call(event);
const scope = { type: "entity", index: Number(event.target.dataset.index) };
const stopIndex = Number(event.target.dataset.stopIndex);
this._commitGradientStopPosEdit(scope, stopIndex, event.target.value, event.target);
return;
}
if (kind === "gradient-draft-pos") {
(_i = event.preventDefault) == null ? void 0 : _i.call(event);
this._commitGradientStopDraft({ type: "card" });
return;
}
if (kind === "gradient-draft-color") {
(_j = event.preventDefault) == null ? void 0 : _j.call(event);
this._commitGradientStopDraft({ type: "card" });
return;
}
if (kind === "entity-gradient-draft-pos") {
(_k = event.preventDefault) == null ? void 0 : _k.call(event);
this._commitGradientStopDraft({ type: "entity", index: Number(event.target.dataset.index) });
return;
}
if (kind === "entity-gradient-draft-color") {
(_l = event.preventDefault) == null ? void 0 : _l.call(event);
this._commitGradientStopDraft({ type: "entity", index: Number(event.target.dataset.index) });
return;
}
if (kind === "segment-from" || kind === "segment-to") {
(_m = event.preventDefault) == null ? void 0 : _m.call(event);
this._commitSegmentBoundaryEdit({ type: "card" }, Number(event.target.dataset.index), kind === "segment-from" ? "from" : "to", event.target.value, event.target);
return;
}
if (kind === "entity-segment-from" || kind === "entity-segment-to") {
(_n = event.preventDefault) == null ? void 0 : _n.call(event);
this._commitSegmentBoundaryEdit({ type: "entity", index: Number(event.target.dataset.index) }, Number(event.target.dataset.segmentIndex), kind === "entity-segment-from" ? "from" : "to", event.target.value, event.target);
return;
}
if (kind === "segment-draft-from" || kind === "segment-draft-to" || kind === "segment-draft-color") {
(_o = event.preventDefault) == null ? void 0 : _o.call(event);
this._commitSegmentDraft({ type: "card" });
return;
}
if (kind === "entity-segment-draft-from" || kind === "entity-segment-draft-to" || kind === "entity-segment-draft-color") {
(_p = event.preventDefault) == null ? void 0 : _p.call(event);
this._commitSegmentDraft({ type: "entity", index: Number(event.target.dataset.index) });
}
}
_handleFieldEvent(event) {
var _a, _b, _c, _d, _e, _f, _g;
const target = event.target;
const rawField = (_a = target == null ? void 0 : target.dataset) == null ? void 0 : _a.field;
const rawKind = (_b = target == null ? void 0 : target.dataset) == null ? void 0 : _b.kind;
const field = (rawField == null ? void 0 : rawField.endsWith("-text-fallback")) ? rawField.slice(0, -14) : rawField;
const kind = (rawKind == null ? void 0 : rawKind.endsWith("-text-fallback")) ? rawKind.slice(0, -14) : rawKind;
const detailValue = (_c = event.detail) == null ? void 0 : _c.value;
const value = detailValue != null ? detailValue : (target == null ? void 0 : target.type) === "checkbox" ? target.checked : target == null ? void 0 : target.value;
if (kind === "entity-markers-inherit") {
const scope = { type: "entity", index: Number(target.dataset.index) };
this._resetGenericMarkerUiScope(scope);
if (value) this._removeScopedValue(scope, ["markers"], { rerender: true });
else this._setGenericMarkerList(scope, this._getGenericMarkers(scope), { rerender: true });
return;
}
if (kind == null ? void 0 : kind.startsWith("generic-marker-")) {
this._setGenericMarkerField(
this._getGenericMarkerScope(target),
Number(target.dataset.markerIndex),
kind,
value
);
return;
}
if (field === "title") return void this._setTitle(value);
if (field === "formatting-unit") return void this._setScopedFormattingUnit({ type: "card" }, value);
if (field === "formatting-decimal") return void this._setScopedFormattingDecimal({ type: "card" }, value);
if (field === "layout-label-position") return void this._setLayoutLabelPosition(value);
if (field === "layout-label-hero-size") return void this._setLayoutHeroSize(value);
if (field === "layout-hero-value-size") return void this._setLayoutHeroValueSize(value);
if (field === "layout-height") return void this._setLayoutHeight(value);
if (field === "layout-label-width") return void this._setScopedLayoutLabelWidth({ type: "card" }, value);
if (field === "scale-min") return void this._setScaleBound("min", value);
if (field === "scale-max") return void this._setScaleBound("max", value);
if (field === "bar-fill-style") return void this._setBarFillStyle(value);
if (field === "bar-color") return void this._setBarColor(value);
if (field === "bar-solid-fill") return void this._setScopedBarSolidFill({ type: "card" }, value);
if (field === "bar-needle-mode") return void this._setScopedNeedleMode({ type: "card" }, value);
if (field === "bar-needle-color") return void this._setScopedNeedleColor({ type: "card" }, value);
if (field === "baseline-mode") return void this._setBaselineMode({ type: "card" }, value);
if (field === "baseline-value") {
return void this._setBaselineResolvablePart({ type: "card" }, "fixed", value);
}
if (field === "baseline-above-color") return void this._setBaselineDirectionalColor({ type: "card" }, "above", value);
if (field === "baseline-below-color") return void this._setBaselineDirectionalColor({ type: "card" }, "below", value);
if (field === "baseline-above-color-enabled") return void this._setBaselineDirectionalColorEnabled({ type: "card" }, "above", value);
if (field === "baseline-below-color-enabled") return void this._setBaselineDirectionalColorEnabled({ type: "card" }, "below", value);
if (field === "target-mode") return void this._setTargetMode({ type: "card" }, value);
if (field === "target-value") {
return void this._setTargetResolvablePart({ type: "card" }, "fixed", value);
}
if (field === "target-shape") return void this._setTargetShape({ type: "card" }, value);
if (field === "target-direction") return void this._setMarkerDirection({ type: "card" }, "target", value);
if (field === "target-color") return void this._setTargetColor({ type: "card" }, value);
if ((field == null ? void 0 : field.startsWith("target-label-")) || (field == null ? void 0 : field.startsWith("peak-label-")) || (field == null ? void 0 : field.startsWith("floor-label-"))) {
const key = field.startsWith("target-") ? "target" : field.startsWith("peak-") ? "peak" : "floor";
const option = field.slice(`${key}-label-`.length);
const scope = { type: "card" };
if (option === "show") {
this._setBuiltinMarkerLabelField(scope, key, "show", value);
} else {
const labelField = option === "show-value" ? "showValue" : option === "show-unit" ? "showUnit" : option;
this._setBuiltinMarkerLabelField(scope, key, labelField, value);
}
return;
}
if (field === "target-above-fill-enabled") return void this._setTargetAboveFillEnabled({ type: "card" }, value);
if (field === "target-above-fill-color") return void this._setTargetAboveFillColor({ type: "card" }, value);
if (field === "peak-show") return void this._setPeakShow(value);
if (field === "peak-color") return void this._setScopedPeakColor({ type: "card" }, value);
if (field === "peak-direction") return void this._setMarkerDirection({ type: "card" }, "peak", value);
if (field === "peak-reset") return void this._setScopedExtremumReset({ type: "card" }, "peak", value);
if (field === "floor-show") return void this._setScopedExtremumEnabled({ type: "card" }, "floor", value);
if (field === "floor-color") return void this._setScopedExtremumColor({ type: "card" }, "floor", value);
if (field === "floor-direction") return void this._setMarkerDirection({ type: "card" }, "floor", value);
if (field === "floor-reset") return void this._setScopedExtremumReset({ type: "card" }, "floor", value);
if (kind === "entity-picker" || kind === "entity-input") {
const index = Number(target.dataset.index);
const nextEntities = this._getEntitiesValue().map((entry, entryIndex) => entryIndex === index ? { ...entry, entity: this._normalizeTextValue(value) } : entry);
const nextEntries = this._buildEntityConfigEntries(nextEntities);
if (Array.isArray(this._draftConfig.entities) || nextEntries.length > 1 || !this._draftConfig.entity) {
this._setValueAtPath(["entities"], nextEntries);
} else {
this._setValueAtPath(["entity"], (_e = (_d = nextEntities[0]) == null ? void 0 : _d.entity) != null ? _e : "");
}
return;
}
if (kind === "entity-name") {
return void this._setEntityField(Number(target.dataset.index), "name", value);
}
if (kind === "entity-icon") {
return void this._setEntityField(Number(target.dataset.index), "icon", value);
}
if (kind === "scale-min-entity-source") {
return void this._setCanonicalResolvablePart({ type: "card" }, "min", "entity", value);
}
if (kind === "scale-max-entity-source") {
return void this._setCanonicalResolvablePart({ type: "card" }, "max", "entity", value);
}
if (kind === "baseline-entity-source") {
return void this._setBaselineResolvablePart({ type: "card" }, "entity", value);
}
if (kind === "target-entity-source") {
return void this._setTargetResolvablePart({ type: "card" }, "entity", value);
}
if (kind === "entity-scale-inherit") {
if (value) {
return void this._clearScaleOverride({ type: "entity", index: Number(target.dataset.index) });
}
return;
}
if (kind === "entity-override-min") {
return void this._setCanonicalResolvablePart({ type: "entity", index: Number(target.dataset.index) }, "min", "fixed", value);
}
if (kind === "entity-override-max") {
return void this._setCanonicalResolvablePart({ type: "entity", index: Number(target.dataset.index) }, "max", "fixed", value);
}
if (kind === "entity-override-min-entity-source") {
return void this._setCanonicalResolvablePart({ type: "entity", index: Number(target.dataset.index) }, "min", "entity", value);
}
if (kind === "entity-override-max-entity-source") {
return void this._setCanonicalResolvablePart({ type: "entity", index: Number(target.dataset.index) }, "max", "entity", value);
}
if (kind === "entity-override-height") {
return void this._setScopedLayoutHeight({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-layout-inherit") {
if (value) {
return void this._clearLayoutOverride({ type: "entity", index: Number(target.dataset.index) });
}
return;
}
if (kind === "entity-layout-label-position") {
return void this._setScopedLayoutLabelPosition({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-layout-label-hero-size") {
return void this._setScopedLayoutHeroSize({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-layout-hero-value-size") {
return void this._setScopedLayoutHeroValueSize({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-layout-label-width") {
return void this._setScopedLayoutLabelWidth({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-formatting-inherit") {
if (value) {
return void this._clearFormattingOverride({ type: "entity", index: Number(target.dataset.index) });
}
return;
}
if (kind === "entity-formatting-unit") {
return void this._setScopedFormattingUnit({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-formatting-decimal") {
return void this._setScopedFormattingDecimal({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-peak-inherit") {
if (value) {
return void this._clearPeakOverride({ type: "entity", index: Number(target.dataset.index) });
}
return;
}
if (kind === "entity-peak-enabled") {
return void this._setScopedPeakEnabled({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-peak-color") {
return void this._setScopedPeakColor({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-peak-direction") {
return void this._setMarkerDirection({ type: "entity", index: Number(target.dataset.index) }, "peak", value);
}
if (kind === "entity-peak-reset") {
return void this._setScopedExtremumReset({ type: "entity", index: Number(target.dataset.index) }, "peak", value);
}
const entityLabelMatch = kind == null ? void 0 : kind.match(/^entity-(target|peak|floor)-label-(show|text|show-value|show-unit|precision)$/);
if (entityLabelMatch) {
const [, key, option] = entityLabelMatch;
const scope = { type: "entity", index: Number(target.dataset.index) };
if (option === "show") {
this._setBuiltinMarkerLabelField(scope, key, "show", value);
} else {
this._setBuiltinMarkerLabelField(scope, key, option === "show-value" ? "showValue" : option === "show-unit" ? "showUnit" : option, value);
}
return;
}
if (kind === "entity-floor-inherit") {
if (value) return void this._clearFloorOverride({ type: "entity", index: Number(target.dataset.index) });
return;
}
if (kind === "entity-floor-enabled") {
return void this._setScopedExtremumEnabled({ type: "entity", index: Number(target.dataset.index) }, "floor", value);
}
if (kind === "entity-floor-color") {
return void this._setScopedExtremumColor({ type: "entity", index: Number(target.dataset.index) }, "floor", value);
}
if (kind === "entity-floor-direction") {
return void this._setMarkerDirection({ type: "entity", index: Number(target.dataset.index) }, "floor", value);
}
if (kind === "entity-floor-reset") {
return void this._setScopedExtremumReset({ type: "entity", index: Number(target.dataset.index) }, "floor", value);
}
if (kind === "entity-segments-inherit") {
if (value) {
return void this._clearSegmentsOverride({ type: "entity", index: Number(target.dataset.index) });
}
return;
}
if (kind === "entity-gradient-stops-inherit") {
if (value) {
return void this._clearGradientStopsOverride({ type: "entity", index: Number(target.dataset.index) });
}
return;
}
if (kind === "entity-bar-inherit") {
if (value) {
return void this._clearEntityBarAppearance({ type: "entity", index: Number(target.dataset.index) });
}
return;
}
if (kind === "entity-needle-inherit") {
if (value) {
return void this._removeScopedNeedle({ type: "entity", index: Number(target.dataset.index) });
}
return;
}
if (kind === "entity-bar-fill-style") {
return void this._setScopedBarFillStyle({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-bar-color") {
return void this._setScopedBarColor({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-bar-solid-fill") {
return void this._setScopedBarSolidFill({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-needle-mode") {
return void this._setScopedNeedleMode({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-needle-color") {
return void this._setScopedNeedleColor({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-baseline-mode") {
return void this._setBaselineMode({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-baseline-inherit") {
if (value) {
return void this._clearBaselineOverride({ type: "entity", index: Number(target.dataset.index) });
}
return;
}
if (kind === "entity-baseline-value") {
return void this._setBaselineResolvablePart({ type: "entity", index: Number(target.dataset.index) }, "fixed", value);
}
if (kind === "entity-baseline-entity-source") {
return void this._setBaselineResolvablePart({ type: "entity", index: Number(target.dataset.index) }, "entity", value);
}
if (kind === "entity-baseline-above-color") {
return void this._setBaselineDirectionalColor({ type: "entity", index: Number(target.dataset.index) }, "above", value);
}
if (kind === "entity-baseline-below-color") {
return void this._setBaselineDirectionalColor({ type: "entity", index: Number(target.dataset.index) }, "below", value);
}
if (kind === "entity-baseline-above-color-enabled") {
return void this._setBaselineDirectionalColorEnabled({ type: "entity", index: Number(target.dataset.index) }, "above", value);
}
if (kind === "entity-baseline-below-color-enabled") {
return void this._setBaselineDirectionalColorEnabled({ type: "entity", index: Number(target.dataset.index) }, "below", value);
}
if (kind === "entity-target-mode") {
return void this._setTargetMode({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-target-inherit") {
if (value) {
return void this._clearTargetOverride({ type: "entity", index: Number(target.dataset.index) });
}
return;
}
if (kind === "entity-target-value") {
return void this._setTargetResolvablePart({ type: "entity", index: Number(target.dataset.index) }, "fixed", value);
}
if (kind === "entity-target-shape") {
return void this._setTargetShape({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-target-direction") {
return void this._setMarkerDirection({ type: "entity", index: Number(target.dataset.index) }, "target", value);
}
if (kind === "entity-target-entity-source") {
return void this._setTargetResolvablePart({ type: "entity", index: Number(target.dataset.index) }, "entity", value);
}
if (kind === "entity-target-color") {
return void this._setTargetColor({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-target-above-fill-enabled") {
return void this._setTargetAboveFillEnabled({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "entity-target-above-fill-color") {
return void this._setTargetAboveFillColor({ type: "entity", index: Number(target.dataset.index) }, value);
}
if (kind === "gradient-draft-pos") {
this._setGradientStopsDraftField({ type: "card" }, "pos", value);
return;
}
if (kind === "gradient-draft-color") {
this._setGradientStopsDraftField({ type: "card" }, "color", value);
return;
}
if (kind === "entity-gradient-draft-pos") {
this._setGradientStopsDraftField({ type: "entity", index: Number(target.dataset.index) }, "pos", value);
return;
}
if (kind === "entity-gradient-draft-color") {
this._setGradientStopsDraftField({ type: "entity", index: Number(target.dataset.index) }, "color", value);
return;
}
if (kind === "segment-draft-from") {
this._setSegmentDraftField({ type: "card" }, "from", value);
return;
}
if (kind === "segment-draft-to") {
this._setSegmentDraftField({ type: "card" }, "to", value);
return;
}
if (kind === "segment-draft-color") {
this._setSegmentDraftField({ type: "card" }, "color", value);
return;
}
if (kind === "entity-segment-draft-from") {
this._setSegmentDraftField({ type: "entity", index: Number(target.dataset.index) }, "from", value);
return;
}
if (kind === "entity-segment-draft-to") {
this._setSegmentDraftField({ type: "entity", index: Number(target.dataset.index) }, "to", value);
return;
}
if (kind === "entity-segment-draft-color") {
this._setSegmentDraftField({ type: "entity", index: Number(target.dataset.index) }, "color", value);
return;
}
if (kind == null ? void 0 : kind.startsWith("gradient-")) {
const index = Number(target.dataset.index);
const currentStops = this._sanitizeGradientStopsForEmit(this._getGradientStopsValue());
const nextStops = this._getGradientStopsValue().map((stop, stopIndex) => {
var _a2;
if (stopIndex !== index) return stop;
const nextPos = this._normalizeGradientStopPosValue(stop == null ? void 0 : stop.pos);
return {
...stop,
pos: nextPos != null ? nextPos : 0,
color: kind === "gradient-color" ? value : (_a2 = stop == null ? void 0 : stop.color) != null ? _a2 : "#4a9eff"
};
});
if (this._serializeConfig(nextStops) !== this._serializeConfig(currentStops)) {
this._setGradientStops(nextStops);
}
return;
}
if (kind == null ? void 0 : kind.startsWith("entity-gradient-")) {
const scope = { type: "entity", index: Number(target.dataset.index) };
const stopIndex = Number(target.dataset.stopIndex);
const currentStops = this._sanitizeGradientStopsForEmit(this._getScopedGradientStopsValue(scope));
const nextStops = this._getScopedGradientStopsValue(scope).map((stop, currentStopIndex) => {
var _a2;
if (currentStopIndex !== stopIndex) return stop;
const nextPos = this._normalizeGradientStopPosValue(stop == null ? void 0 : stop.pos);
return {
...stop,
pos: nextPos != null ? nextPos : 0,
color: kind === "entity-gradient-color" ? value : (_a2 = stop == null ? void 0 : stop.color) != null ? _a2 : "#4a9eff"
};
});
if (this._serializeConfig(nextStops) !== this._serializeConfig(currentStops)) {
this._setScopedGradientStops(scope, nextStops);
}
return;
}
if (kind === "segment-color") {
const index = Number(target.dataset.index);
const nextSegments = ((_f = this._getSegmentsUiRows({ type: "card" })) != null ? _f : this._getSegmentsValue()).map((segment, segmentIndex) => {
if (segmentIndex !== index) return segment;
return {
...segment,
color: value
};
});
this._setSegments(nextSegments, { sort: false });
return;
}
if (kind === "entity-segment-color") {
const scope = { type: "entity", index: Number(target.dataset.index) };
const segmentIndex = Number(target.dataset.segmentIndex);
const nextSegments = ((_g = this._getSegmentsUiRows(scope)) != null ? _g : this._getScopedSegmentsValue(scope)).map((segment, currentSegmentIndex) => {
if (currentSegmentIndex !== segmentIndex) return segment;
return {
...segment,
color: value
};
});
this._setScopedSegments(scope, nextSegments, { sort: false });
return;
}
}
};
}
});
// src/sensor-bar-card-plus.js
var require_sensor_bar_card_plus = __commonJS({
"src/sensor-bar-card-plus.js"() {
init_SensorBarCard();
init_SensorBarCardPlusEditor();
customElements.define("sensor-bar-card-plus", SensorBarCard);
customElements.define("sensor-bar-card-plus-editor", SensorBarCardPlusEditor);
window.customCards = window.customCards || [];
window.customCards.push({
type: "sensor-bar-card-plus",
name: "Sensor Bar Card Plus",
description: "Animated, colour-coded horizontal bar card for Home Assistant with extended target and layout features."
});
}
});
require_sensor_bar_card_plus();
})();