{"version":3,"file":"size-aware-label.cjs","sources":["../../src/util/size-aware-label.ts"],"sourcesContent":["import h from \"@macrostrat/hyper\";\nimport { useRef, useEffect, useState } from \"react\";\nimport classNames from \"classnames\";\n\nexport interface Clickable {\n  onClick?: (evt: MouseEvent) => void;\n}\n\ninterface ElementSize {\n  width: number;\n  height: number;\n}\n\nfunction refSize(ref: React.RefObject<HTMLElement>): ElementSize {\n  const { width, height } = ref.current?.getBoundingClientRect();\n  return { width, height };\n}\n\nexport type SizeAwareLabelProps = React.HTMLProps<\"div\"> &\n  Clickable & {\n    label: React.ReactNode;\n    labelClassName: string;\n    isShown?: boolean;\n    onClick?: (evt: MouseEvent) => void;\n    positionTolerance?: number;\n    allowRotation?: boolean;\n    /** An extra value folded into the fit-measurement dependencies. Change it to\n     * force a re-measure without the label text changing — e.g. pass a flag that\n     * flips when an animation settles, so the label re-fits to its final size\n     * (and not on every intermediate frame). */\n    remeasureKey?: unknown;\n    onVisibilityChanged?(\n      fits: boolean,\n      containerSize: ElementSize,\n      labelSize: ElementSize,\n    ): void;\n  };\n\nfunction SizeAwareLabel(props: SizeAwareLabelProps) {\n  /** A label that only renders if it fits within its container div.\n   * This helps build unit and interval labels that do sensible things\n   * when the container is too small.\n   *\n   * You can use the onUpdate and isShown props to make this a \"controlled\"\n   * component, or you can use the internally managed state. `onUpdate` can\n   * be used by itself when you need to report back whether the label was\n   * rendered or not (e.g., so you can render it in another location).\n   */\n  const {\n    label,\n    isShown,\n    onVisibilityChanged,\n    className,\n    labelClassName,\n    onClick,\n    positionTolerance = 0,\n    allowRotation = false,\n    remeasureKey,\n    ...rest\n  } = props;\n  const containerRef = useRef<HTMLElement>();\n  const labelRef = useRef<HTMLElement>();\n  const [fits, setFits] = useState<boolean | null>(null);\n  const [rotated, setRotated] = useState(false);\n  useEffect(() => {\n    const containerSz = refSize(containerRef);\n    const labelSz = refSize(labelRef);\n    let doesFit =\n      labelSz.width <= containerSz.width + 2 * positionTolerance &&\n      labelSz.height <= containerSz.height + 2 * positionTolerance;\n    if (allowRotation) {\n      if (!doesFit) {\n        // Try rotating the label\n        const rotatedLabelSz = {\n          width: labelSz.height,\n          height: labelSz.width,\n        };\n        const rotatedFits =\n          rotatedLabelSz.width <= containerSz.width + 2 * positionTolerance &&\n          rotatedLabelSz.height <= containerSz.height + 2 * positionTolerance;\n        if (rotatedFits) {\n          doesFit = true;\n          setRotated(true);\n        } else {\n          setRotated(false);\n        }\n      }\n    }\n    setFits(doesFit);\n  }, [containerRef, labelRef, label, positionTolerance, allowRotation, remeasureKey]);\n\n  // Report whether label fits upwards, if needed\n  useEffect(() => {\n    if (fits == null) return;\n    onVisibilityChanged?.(fits, refSize(containerRef), refSize(labelRef));\n  }, [fits]);\n\n  const shouldShow = isShown ?? fits ?? true;\n\n  return h(\n    \"div.label-container\",\n    { ...rest, className, ref: containerRef, onClick },\n    h(\n      \"span.label\",\n      {\n        className: classNames(labelClassName, { rotated }),\n        ref: labelRef,\n        style: {\n          visibility: shouldShow ? \"visible\" : \"hidden\",\n        },\n      },\n      h(\n        \"span.label-text\",\n        {\n          style: {\n            transform: rotated ? \"rotate(-90deg)\" : undefined,\n          },\n        },\n        label,\n      ),\n    ),\n  );\n}\n\nexport { SizeAwareLabel };\n"],"names":["useRef","useState","useEffect","h","classNames"],"mappings":";;;;;;;;AAaA,SAAS,QAAQ,KAAgD;AAC/D,QAAM,EAAE,OAAO,OAAA,IAAW,IAAI,SAAS,sBAAA;AACvC,SAAO,EAAE,OAAO,OAAA;AAClB;AAsBA,SAAS,eAAe,OAA4B;AAUlD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,oBAAoB;AAAA,IACpB,gBAAgB;AAAA,IAChB;AAAA,IACA,GAAG;AAAA,EAAA,IACD;AACJ,QAAM,eAAeA,MAAAA,OAAA;AACrB,QAAM,WAAWA,MAAAA,OAAA;AACjB,QAAM,CAAC,MAAM,OAAO,IAAIC,MAAAA,SAAyB,IAAI;AACrD,QAAM,CAAC,SAAS,UAAU,IAAIA,MAAAA,SAAS,KAAK;AAC5CC,QAAAA,UAAU,MAAM;AACd,UAAM,cAAc,QAAQ,YAAY;AACxC,UAAM,UAAU,QAAQ,QAAQ;AAChC,QAAI,UACF,QAAQ,SAAS,YAAY,QAAQ,IAAI,qBACzC,QAAQ,UAAU,YAAY,SAAS,IAAI;AAC7C,QAAI,eAAe;AACjB,UAAI,CAAC,SAAS;AAEZ,cAAM,iBAAiB;AAAA,UACrB,OAAO,QAAQ;AAAA,UACf,QAAQ,QAAQ;AAAA,QAAA;AAElB,cAAM,cACJ,eAAe,SAAS,YAAY,QAAQ,IAAI,qBAChD,eAAe,UAAU,YAAY,SAAS,IAAI;AACpD,YAAI,aAAa;AACf,oBAAU;AACV,qBAAW,IAAI;AAAA,QACjB,OAAO;AACL,qBAAW,KAAK;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AACA,YAAQ,OAAO;AAAA,EACjB,GAAG,CAAC,cAAc,UAAU,OAAO,mBAAmB,eAAe,YAAY,CAAC;AAGlFA,QAAAA,UAAU,MAAM;AACd,QAAI,QAAQ,KAAM;AAClB,0BAAsB,MAAM,QAAQ,YAAY,GAAG,QAAQ,QAAQ,CAAC;AAAA,EACtE,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,aAAa,WAAW,QAAQ;AAEtC,SAAOC,WAAAA;AAAAA,IACL;AAAA,IACA,EAAE,GAAG,MAAM,WAAW,KAAK,cAAc,QAAA;AAAA,IACzCA,WAAAA;AAAAA,MACE;AAAA,MACA;AAAA,QACE,WAAWC,oBAAAA,QAAW,gBAAgB,EAAE,SAAS;AAAA,QACjD,KAAK;AAAA,QACL,OAAO;AAAA,UACL,YAAY,aAAa,YAAY;AAAA,QAAA;AAAA,MACvC;AAAA,MAEFD,WAAAA;AAAAA,QACE;AAAA,QACA;AAAA,UACE,OAAO;AAAA,YACL,WAAW,UAAU,mBAAmB;AAAA,UAAA;AAAA,QAC1C;AAAA,QAEF;AAAA,MAAA;AAAA,IACF;AAAA,EACF;AAEJ;;"}