{"version":3,"file":"timeFunctions.mjs","names":[],"sources":["../../../../package/common/timeInput/timeFunctions.ts"],"sourcesContent":["export interface TimeValue {\n  hours: number; // 0–23\n  minutes: number; // 0–59\n}\n\nexport const clampHour = (h: number): number => ((Math.trunc(h) % 24) + 24) % 24;\nexport const clampMinute = (m: number): number => ((Math.trunc(m) % 60) + 60) % 60;\n\n/** Parse a \"HH:MM\" (24-hour) string. Returns null for anything that isn't a valid time. */\nexport function parseTimeString(value: string | null | undefined): TimeValue | null {\n  if (!value) return null;\n  const match = value.match(/^(\\d{1,2}):(\\d{1,2})$/);\n  if (!match) return null;\n  const hours = Number(match[1]);\n  const minutes = Number(match[2]);\n  if (hours > 23 || minutes > 59) return null;\n  return { hours, minutes };\n}\n\n/** Format a TimeValue as a zero-padded \"HH:MM\" string. */\nexport function formatTime(time: TimeValue | null | undefined): string {\n  if (!time) return '';\n  const hh = String(clampHour(time.hours)).padStart(2, '0');\n  const mm = String(clampMinute(time.minutes)).padStart(2, '0');\n  return `${hh}:${mm}`;\n}\n\n/** Snap a minute value to the nearest multiple of `step` (step <= 1 is a no-op). */\nexport function snapMinute(minute: number, step: number): number {\n  if (!step || step <= 1) return clampMinute(minute);\n  return clampMinute(Math.round(clampMinute(minute) / step) * step);\n}\n\nexport const timeToMinutes = (time: TimeValue): number => time.hours * 60 + time.minutes;\n\n/** Clamp a time into [min, max] (both \"HH:MM\" strings, either optional). */\nexport function clampToRange(time: TimeValue, min?: string | null, max?: string | null): TimeValue {\n  let total = timeToMinutes(time);\n  const minVal = parseTimeString(min);\n  const maxVal = parseTimeString(max);\n  if (minVal) total = Math.max(total, timeToMinutes(minVal));\n  if (maxVal) total = Math.min(total, timeToMinutes(maxVal));\n  return { hours: Math.floor(total / 60), minutes: total % 60 };\n}\n\nexport function isWithinRange(time: TimeValue, min?: string | null, max?: string | null): boolean {\n  const total = timeToMinutes(time);\n  const minVal = parseTimeString(min);\n  const maxVal = parseTimeString(max);\n  if (minVal && total < timeToMinutes(minVal)) return false;\n  if (maxVal && total > timeToMinutes(maxVal)) return false;\n  return true;\n}\n\n/**\n * Auto-correct a masked \"HH:MM\" value as the user types: clamp the hour segment to 23\n * and the minute segment to 59 so the field can never hold an impossible time.\n */\nexport function correctTimeMaskValue(value: string): string {\n  const digits = value.replace(/[^\\d]/g, '');\n  if (!digits) return value;\n\n  let hourDigits = digits.slice(0, 2);\n  const minuteDigits = digits.slice(2, 4);\n\n  // First hour digit > 2 → it can only be a single-digit hour (e.g. \"9\" → \"09\").\n  if (hourDigits.length === 1 && Number(hourDigits) > 2) {\n    hourDigits = `0${hourDigits}`;\n  }\n  if (hourDigits.length === 2 && Number(hourDigits) > 23) hourDigits = '23';\n\n  let result = hourDigits;\n  if (minuteDigits.length > 0 || /:/.test(value)) result += ':';\n  if (minuteDigits.length > 0) {\n    const clamped = minuteDigits.length === 2 && Number(minuteDigits) > 59 ? '59' : minuteDigits;\n    result += clamped;\n  }\n  return result;\n}\n\n// ── Clock-dial geometry ─────────────────────────────────────────────────────\n\nexport interface DialPoint {\n  x: number;\n  y: number;\n}\n\n/**\n * Cartesian coordinates for a point on the dial. `slot` is the step index from the\n * top of the clock, going clockwise; `stepDegrees` is the angle between steps\n * (30° for hours, 6° for minutes). Returns coordinates relative to (cx, cy).\n */\nexport function polarPoint(cx: number, cy: number, radius: number, slot: number, stepDegrees: number): DialPoint {\n  const theta = (slot * stepDegrees * Math.PI) / 180;\n  return {\n    x: cx + radius * Math.sin(theta),\n    y: cy - radius * Math.cos(theta),\n  };\n}\n\n/** Rotation (deg) of a hand pointing at `slot`. A 3-o'clock-default hand points up at slot 0. */\nexport const handAngle = (slot: number, stepDegrees: number): number => slot * stepDegrees - 90;\n\n/** Angle (0–360, clockwise from top) of a pointer at (dx, dy) relative to the dial centre. */\nexport function pointerAngle(dx: number, dy: number): number {\n  const deg = (Math.atan2(dx, -dy) * 180) / Math.PI;\n  return (deg + 360) % 360;\n}\n\nexport const pointerRadius = (dx: number, dy: number): number => Math.sqrt(dx * dx + dy * dy);\n"],"mappings":"aAKA,IAAa,EAAa,IAAwB,KAAK,MAAM,CAAC,EAAI,GAAM,IAAM,GACjE,EAAe,IAAwB,KAAK,MAAM,CAAC,EAAI,GAAM,IAAM,GAGhF,SAAgB,EAAgB,EAAoD,CAClF,GAAI,CAAC,EAAO,OAAO,KACnB,IAAM,EAAQ,EAAM,MAAM,uBAAuB,EACjD,GAAI,CAAC,EAAO,OAAO,KACnB,IAAM,EAAQ,OAAO,EAAM,EAAE,EACvB,EAAU,OAAO,EAAM,EAAE,EAE/B,OADI,EAAQ,IAAM,EAAU,GAAW,KAChC,CAAE,QAAO,SAAQ,CAC1B,CAGA,SAAgB,EAAW,EAA4C,CAIrE,OAHK,EAGE,GAFI,OAAO,EAAU,EAAK,KAAK,CAAC,CAAC,CAAC,SAAS,EAAG,GAE3C,EAAG,GADF,OAAO,EAAY,EAAK,OAAO,CAAC,CAAC,CAAC,SAAS,EAAG,GACzC,IAHE,EAIpB,CAGA,SAAgB,EAAW,EAAgB,EAAsB,CAE/D,OAD+B,EAA3B,CAAC,GAAQ,GAAQ,EAAsB,EACxB,KAAK,MAAM,EAAY,CAAM,EAAI,CAAI,EAAI,CADX,CAEnD,CAEA,IAAa,EAAiB,GAA4B,EAAK,MAAQ,GAAK,EAAK,QAGjF,SAAgB,EAAa,EAAiB,EAAqB,EAAgC,CACjG,IAAI,EAAQ,EAAc,CAAI,EACxB,EAAS,EAAgB,CAAG,EAC5B,EAAS,EAAgB,CAAG,EAGlC,OAFI,IAAQ,EAAQ,KAAK,IAAI,EAAO,EAAc,CAAM,CAAC,GACrD,IAAQ,EAAQ,KAAK,IAAI,EAAO,EAAc,CAAM,CAAC,GAClD,CAAE,MAAO,KAAK,MAAM,EAAQ,EAAE,EAAG,QAAS,EAAQ,EAAG,CAC9D,CAiDA,SAAgB,EAAW,EAAY,EAAY,EAAgB,EAAc,EAAgC,CAC/G,IAAM,EAAS,EAAO,EAAc,KAAK,GAAM,IAC/C,MAAO,CACL,EAAG,EAAK,EAAS,KAAK,IAAI,CAAK,EAC/B,EAAG,EAAK,EAAS,KAAK,IAAI,CAAK,CACjC,CACF,CAMA,SAAgB,EAAa,EAAY,EAAoB,CAE3D,OADa,KAAK,MAAM,EAAI,CAAC,CAAE,EAAI,IAAO,KAAK,GACjC,KAAO,GACvB,CAEA,IAAa,GAAiB,EAAY,IAAuB,KAAK,KAAK,EAAK,EAAK,EAAK,CAAE"}