456 files
This commit is contained in:
@@ -151,8 +151,8 @@
|
||||
.tmd-hd .ic{width:32px;height:32px;border-radius:9px;display:grid;place-items:center;background:rgba(255,255,255,.22);font-size:17px;flex:none}
|
||||
.tmd-hd .tt{font-family:var(--tmd-font-display);font-weight:800;font-size:16px;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.tmd-hd .tt small{display:block;font-family:var(--tmd-font-body);font-weight:600;font-size:11px;opacity:.85;margin-top:1px}
|
||||
.tmd-hd .pill{margin-left:auto;font-size:11px;font-weight:800;padding:4px 9px;border-radius:999px;background:rgba(255,255,255,.25);text-transform:capitalize}
|
||||
.tmd-hd .cnt{margin-left:auto;min-width:22px;height:22px;padding:0 6px;border-radius:999px;background:var(--tmd-bad);color:#fff;font-size:12px;font-weight:800;display:grid;place-items:center}
|
||||
.tmd-hd .pill{margin-inline-start:auto;font-size:11px;font-weight:800;padding:4px 9px;border-radius:999px;background:rgba(255,255,255,.25);text-transform:capitalize}
|
||||
.tmd-hd .cnt{margin-inline-start:auto;min-width:22px;height:22px;padding:0 6px;border-radius:999px;background:var(--tmd-bad);color:#fff;font-size:12px;font-weight:800;display:grid;place-items:center}
|
||||
.tmd-bd{padding:15px}
|
||||
.tmd-empty{text-align:center;padding:26px 12px;color:var(--tmd-dim);font-size:.9rem}
|
||||
.av{width:var(--av,42px);height:var(--av,42px);border-radius:50%;flex:none;display:grid;place-items:center;font-family:var(--tmd-font-display);font-weight:800;color:#fff;font-size:calc(var(--av,42px)*.4);line-height:1;text-align:center;background:var(--ac,var(--tmd-accent));box-shadow:0 2px 6px rgba(0,0,0,.18);overflow:hidden}
|
||||
@@ -233,7 +233,7 @@
|
||||
:host([data-tm-design="graphite"]) .tmd-checklist{border:1px solid var(--tmd-border);border-radius:var(--tmd-radius-sm)}
|
||||
:host([data-tm-design="graphite"]) .tmd-check{position:relative;padding:9px 12px;min-height:46px;box-sizing:border-box}
|
||||
:host([data-tm-design="graphite"]) .tmd-check+.tmd-check{border-top:0}
|
||||
:host([data-tm-design="graphite"]) .tmd-check+.tmd-check::before{content:"";position:absolute;top:0;left:46px;right:0;height:1px;background:var(--tmd-border)}
|
||||
:host([data-tm-design="graphite"]) .tmd-check+.tmd-check::before{content:"";position:absolute;top:0;inset-inline-start:46px;inset-inline-end:0;height:1px;background:var(--tmd-border)}
|
||||
:host([data-tm-design="graphite"]) .tmd-check .c-num{width:22px;height:22px;border-radius:50%;
|
||||
background:transparent;border:1.5px solid var(--tmd-border);color:transparent;font-size:0}
|
||||
:host([data-tm-design="graphite"]) .tmd-check.done .c-num{background:var(--tmd-good);border-color:var(--tmd-good);color:#fff;font-size:13px}
|
||||
@@ -306,7 +306,7 @@
|
||||
:host([data-tm-design="graphite"]) .lb-cp-score{font-variant-numeric:tabular-nums;font-weight:600;letter-spacing:-.6px}
|
||||
:host([data-tm-design="graphite"]) .lb-cp-unit{color:var(--tmd-dim);font-weight:400}
|
||||
:host([data-tm-design="graphite"]) .lb-cp-row.win{background:transparent}
|
||||
:host([data-tm-design="graphite"]) .lb-cp-meta ha-icon{--mdc-icon-size:14px;vertical-align:-2px;margin-right:3px;color:var(--tmd-dim)}
|
||||
:host([data-tm-design="graphite"]) .lb-cp-meta ha-icon{--mdc-icon-size:14px;vertical-align:-2px;margin-inline-end:3px;color:var(--tmd-dim)}
|
||||
|
||||
/* Overview: hairline-separated stats, then a grouped per-child list. */
|
||||
:host([data-tm-design="graphite"]) .ov-kids{border:1px solid var(--tmd-border);border-radius:var(--tmd-radius-sm)}
|
||||
@@ -317,7 +317,13 @@
|
||||
:host([data-tm-design="graphite"]) .ov-today .val,
|
||||
:host([data-tm-design="graphite"]) .pts{font-variant-numeric:tabular-nums;font-weight:600;letter-spacing:-.6px}
|
||||
:host([data-tm-design="graphite"]) .nm,
|
||||
:host([data-tm-design="graphite"]) .ttl{font-weight:500;letter-spacing:-.18px}`;
|
||||
:host([data-tm-design="graphite"]) .ttl{font-weight:500;letter-spacing:-.18px}
|
||||
|
||||
/* Right-to-left (#979). apply() stamps dir on the host; an icon that points
|
||||
along the reading direction (chevron, undo, backspace) carries .tm-rtl-flip
|
||||
so it turns round with the layout. Text arrows (→ ↩) swap to their mirrored
|
||||
character instead, which leaves LTR markup untouched. Not design-scoped. */
|
||||
:host([dir="rtl"]) .tm-rtl-flip,[dir="rtl"] .tm-rtl-flip{transform:scaleX(-1)}`;
|
||||
|
||||
if (!document.getElementById("taskmate-design-fonts")) {
|
||||
const styleEl = document.createElement("style");
|
||||
@@ -410,16 +416,60 @@
|
||||
return !!(window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Text direction of HA's current language (#979): "rtl" or "ltr". HA marks
|
||||
* RTL languages in its translation metadata (the same isRTL flag its own
|
||||
* computeRTL reads); for a language it has no metadata for, follow the
|
||||
* direction HA already gave the page.
|
||||
*/
|
||||
let _lastDir = "ltr";
|
||||
function direction(hass) {
|
||||
const lang = hass && hass.language;
|
||||
const meta = hass && hass.translationMetadata && hass.translationMetadata.translations;
|
||||
const root = typeof document !== "undefined" && document.documentElement;
|
||||
if (lang && meta && meta[lang]) _lastDir = meta[lang].isRTL ? "rtl" : "ltr";
|
||||
else _lastDir = root && root.dir === "rtl" ? "rtl" : "ltr";
|
||||
return _lastDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep number runs reading left to right inside right-to-left text (#995).
|
||||
* "3 / 1", "1,200 / 5,000" and "3-day" are one unit each, but the Unicode
|
||||
* bidi algorithm lays their parts out right to left in an RTL paragraph, so
|
||||
* "3 / 1" shows as "1 / 3" and "3-Day Streak" as "Day Streak-3". Each run is
|
||||
* wrapped in a left-to-right isolate, and a label holding one, or opening
|
||||
* with a number ("2 of 5 done", which otherwise reads "of 5 done 2"), is
|
||||
* wrapped in a first-strong isolate so it keeps its own reading order (an
|
||||
* English label stays English-ordered, a translated RTL one stays RTL). The
|
||||
* isolates (U+2066/2068 … U+2069) are invisible, but splitting a text run
|
||||
* still nudges glyph positioning by a sub-pixel, so they are only added
|
||||
* while the page is right-to-left: the direction last resolved by
|
||||
* direction(), which every card calls through apply() at the top of
|
||||
* render() and the panel calls in its hass setter (HA has one language per
|
||||
* page). In LTR, and for text with no number run that doesn't open with a
|
||||
* number, the value comes back untouched. Works on plain strings: Lit text,
|
||||
* the panel's HTML strings and translated messages alike.
|
||||
*/
|
||||
const _NUM = String.raw`\p{Nd}+(?:[.,\u066B\u066C]\p{Nd}+)*`;
|
||||
const _NUM_RUN = new RegExp(String.raw`${_NUM}(?:\s*\/\s*${_NUM})+|(?<![\p{L}-])${_NUM}-\p{L}+`, "gu");
|
||||
function ltrNums(text) {
|
||||
if (text === null || text === undefined || _lastDir !== "rtl") return text;
|
||||
const s = String(text);
|
||||
const out = s.replace(_NUM_RUN, (run) => "\u2066" + run + "\u2069");
|
||||
return out === s && !/^\p{Nd}/u.test(s) ? s : "\u2068" + out + "\u2069";
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the design AND stamp the host element: data-tm-design + (in dark
|
||||
* mode) data-tm-dark. Cards call this once at the top of render() and branch
|
||||
* on the returned id. Returns "classic" when no design applies.
|
||||
* mode) data-tm-dark + dir. Cards call this once at the top of render() and
|
||||
* branch on the returned id. Returns "classic" when no design applies.
|
||||
*/
|
||||
function apply(el, hass, config, entity) {
|
||||
const design = resolve(hass, config, entity);
|
||||
if (el) {
|
||||
el.setAttribute("data-tm-design", design);
|
||||
el.toggleAttribute("data-tm-dark", design !== "classic" && isDark(hass, el));
|
||||
el.setAttribute("dir", direction(hass));
|
||||
}
|
||||
// The host keeps the real id (its CSS keys off it); the card gets the
|
||||
// layout id so aliased styles reuse an existing render path.
|
||||
@@ -487,7 +537,7 @@
|
||||
`;
|
||||
}
|
||||
|
||||
window.__taskmate_design = { IDS, resolve, isDark, apply, active, editorOptions, styles, cssText, tokensCSS: TOKENS, colourPicker };
|
||||
window.__taskmate_design = { IDS, resolve, isDark, direction, ltrNums, apply, active, editorOptions, styles, cssText, tokensCSS: TOKENS, colourPicker };
|
||||
|
||||
/**
|
||||
* The single place that decides what a chore looks like (#750).
|
||||
@@ -541,6 +591,29 @@
|
||||
return { config: Object.assign({}, stub, { type: "custom:" + type, entity: entityId }) };
|
||||
};
|
||||
|
||||
// ── Birthday "first open today" gate (#924) ───────────────────────────────
|
||||
// True the first time it's asked about a child on a given day on this device,
|
||||
// false after that — so the birthday confetti plays once per day, not on every
|
||||
// hass update or page load. Shared by every card that celebrates, so a child
|
||||
// card and a points card on the same dashboard don't both go off. Keyed on
|
||||
// HA's time zone so the day rolls over with the backend, not the browser.
|
||||
window.__taskmate_birthday_once = window.__taskmate_birthday_once || function (hass, childId) {
|
||||
if (!childId) return false;
|
||||
let day;
|
||||
try {
|
||||
day = new Date().toLocaleDateString("en-CA", { timeZone: hass?.config?.time_zone || undefined });
|
||||
} catch (e) {
|
||||
day = new Date().toLocaleDateString("en-CA");
|
||||
}
|
||||
const KEY = "taskmate_celebrated_day";
|
||||
let seen;
|
||||
try { seen = JSON.parse(window.localStorage.getItem(KEY) || "{}") || {}; } catch (e) { seen = {}; }
|
||||
if (seen[childId] === day) return false;
|
||||
seen[childId] = day;
|
||||
try { window.localStorage.setItem(KEY, JSON.stringify(seen)); } catch (e) { /* private mode: plays each load */ }
|
||||
return true;
|
||||
};
|
||||
|
||||
// ── Chore-photo lightbox ───────────────────────────────────────────────────
|
||||
// A single shared, design-AGNOSTIC image viewer for evidence photos. It is
|
||||
// mounted on document.body (NOT inside any card's shadow root), so it paints
|
||||
@@ -582,7 +655,7 @@
|
||||
"background:#111;box-shadow:0 18px 60px rgba(0,0,0,.6);}" +
|
||||
".tm-lightbox .tm-lb-cap{color:#f4f6fb;font-size:14px;font-weight:600;text-align:center;" +
|
||||
"max-width:92vw;line-height:1.4;text-shadow:0 1px 2px rgba(0,0,0,.6);}" +
|
||||
".tm-lightbox .tm-lb-close{position:absolute;top:14px;right:16px;width:42px;height:42px;border:none;" +
|
||||
".tm-lightbox .tm-lb-close{position:absolute;top:14px;inset-inline-end:16px;width:42px;height:42px;border:none;" +
|
||||
"border-radius:50%;background:rgba(255,255,255,.14);color:#fff;font-size:22px;line-height:1;" +
|
||||
"cursor:pointer;transition:background .15s;}" +
|
||||
".tm-lightbox .tm-lb-close:hover{background:rgba(255,255,255,.28);}" +
|
||||
|
||||
Reference in New Issue
Block a user