@stackone/malachite 2.63.0 → 2.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,51 +1,34 @@
1
- import { jsxs as t, jsx as e } from "react/jsx-runtime";
2
- import c from "./CustomIcons/DismissIcon.js";
3
- const r = ({
4
- onClose: i,
5
- className: s = ""
6
- }) => /* @__PURE__ */ e(
7
- "button",
8
- {
9
- type: "button",
10
- "aria-label": "Close",
11
- onClick: i,
12
- className: `flex cursor-pointer items-center justify-center border-0 bg-transparent p-0 transition-colors ${s}`,
13
- children: /* @__PURE__ */ e(c, { size: 12 })
14
- }
15
- ), x = ({
16
- left: i,
17
- right: s,
18
- logo: a,
1
+ import { jsxs as i, jsx as e } from "react/jsx-runtime";
2
+ import { CloseButton as s } from "./CloseButton.js";
3
+ const f = ({
4
+ left: a,
5
+ right: n,
6
+ logo: r,
19
7
  onClose: l,
20
- footer: n
21
- }) => /* @__PURE__ */ t("div", { className: "flex h-screen w-full overflow-hidden gap-3 bg-[var(--malachite-card-sunken-background)] p-3", children: [
22
- /* @__PURE__ */ t("div", { className: "flex min-w-0 flex-1 flex-col items-center px-[60px] py-[40px]", children: [
23
- /* @__PURE__ */ t("div", { className: "flex h-6 w-full shrink-0 items-center justify-between", children: [
8
+ footer: c
9
+ }) => /* @__PURE__ */ i("div", { className: "flex h-screen w-full overflow-hidden gap-3 bg-[var(--malachite-card-sunken-background)] p-3", children: [
10
+ /* @__PURE__ */ i("div", { className: "flex min-w-0 flex-1 flex-col items-center px-[60px] py-[40px]", children: [
11
+ /* @__PURE__ */ i("div", { className: "flex h-6 w-full shrink-0 items-center justify-between", children: [
24
12
  /* @__PURE__ */ e("span", { className: "size-6 shrink-0", "aria-hidden": !0 }),
25
- a ?? /* @__PURE__ */ e("span", { className: "size-6 shrink-0", "aria-hidden": !0 }),
26
- /* @__PURE__ */ e("span", { className: "flex size-6 shrink-0 items-center justify-center", children: l ? /* @__PURE__ */ e(
27
- r,
28
- {
29
- onClose: l,
30
- className: "text-[var(--malachite-text-secondary)] hover:text-[var(--malachite-text-primary)] lg:hidden"
31
- }
32
- ) : null })
13
+ r ?? /* @__PURE__ */ e("span", { className: "size-6 shrink-0", "aria-hidden": !0 }),
14
+ /* @__PURE__ */ e("span", { className: "flex size-6 shrink-0 items-center justify-center", children: l ? /* @__PURE__ */ e(s, { onClick: l, className: "lg:hidden" }) : null })
33
15
  ] }),
34
- /* @__PURE__ */ e("div", { className: "flex w-full min-h-0 flex-1 flex-col overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: /* @__PURE__ */ e("div", { className: "m-auto flex w-full max-w-[380px] flex-col items-center py-10", children: i }) }),
35
- /* @__PURE__ */ e("div", { className: "flex h-6 shrink-0 items-center justify-center", children: n })
16
+ /* @__PURE__ */ e("div", { className: "flex w-full min-h-0 flex-1 flex-col overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: /* @__PURE__ */ e("div", { className: "m-auto flex w-full max-w-[380px] flex-col items-center py-10", children: a }) }),
17
+ /* @__PURE__ */ e("div", { className: "flex h-6 shrink-0 items-center justify-center", children: c })
36
18
  ] }),
37
- /* @__PURE__ */ t("div", { className: "relative hidden min-w-0 flex-1 flex-col items-center justify-center overflow-hidden rounded-[28px] bg-[#2b2b2b] px-[60px] py-[40px] lg:flex", children: [
19
+ /* @__PURE__ */ i("div", { className: "relative hidden min-w-0 flex-1 flex-col items-center justify-center overflow-hidden rounded-[28px] bg-[#2b2b2b] px-[60px] py-[40px] lg:flex", children: [
38
20
  l ? /* @__PURE__ */ e(
39
- r,
21
+ s,
40
22
  {
41
- onClose: l,
42
- className: "absolute top-[40px] right-[60px] z-10 text-white/70 hover:text-white"
23
+ onClick: l,
24
+ surface: "inverse",
25
+ className: "absolute top-[34px] right-[54px] z-10"
43
26
  }
44
27
  ) : null,
45
- s
28
+ n
46
29
  ] })
47
30
  ] });
48
31
  export {
49
- x as default
32
+ f as default
50
33
  };
51
34
  //# sourceMappingURL=SplitScreenLayout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SplitScreenLayout.js","sources":["../../src/components/SplitScreenLayout.tsx"],"sourcesContent":["import React from 'react';\nimport DismissIcon from './CustomIcons/DismissIcon';\n\ninterface SplitScreenLayoutProps {\n left: React.ReactNode;\n right: React.ReactNode;\n logo?: React.ReactNode;\n onClose?: () => void;\n footer?: React.ReactNode;\n}\n\nconst CloseButton: React.FC<{ onClose: () => void; className?: string }> = ({\n onClose,\n className = '',\n}) => (\n <button\n type=\"button\"\n aria-label=\"Close\"\n onClick={onClose}\n className={`flex cursor-pointer items-center justify-center border-0 bg-transparent p-0 transition-colors ${className}`}\n >\n <DismissIcon size={12} />\n </button>\n);\n\nconst SplitScreenLayout: React.FC<SplitScreenLayoutProps> = ({\n left,\n right,\n logo,\n onClose,\n footer,\n}) => (\n <div className=\"flex h-screen w-full overflow-hidden gap-3 bg-[var(--malachite-card-sunken-background)] p-3\">\n <div className=\"flex min-w-0 flex-1 flex-col items-center px-[60px] py-[40px]\">\n <div className=\"flex h-6 w-full shrink-0 items-center justify-between\">\n <span className=\"size-6 shrink-0\" aria-hidden />\n {logo ?? <span className=\"size-6 shrink-0\" aria-hidden />}\n <span className=\"flex size-6 shrink-0 items-center justify-center\">\n {onClose ? (\n <CloseButton\n onClose={onClose}\n className=\"text-[var(--malachite-text-secondary)] hover:text-[var(--malachite-text-primary)] lg:hidden\"\n />\n ) : null}\n </span>\n </div>\n <div className=\"flex w-full min-h-0 flex-1 flex-col overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\">\n <div className=\"m-auto flex w-full max-w-[380px] flex-col items-center py-10\">\n {left}\n </div>\n </div>\n <div className=\"flex h-6 shrink-0 items-center justify-center\">{footer}</div>\n </div>\n <div className=\"relative hidden min-w-0 flex-1 flex-col items-center justify-center overflow-hidden rounded-[28px] bg-[#2b2b2b] px-[60px] py-[40px] lg:flex\">\n {onClose ? (\n <CloseButton\n onClose={onClose}\n className=\"absolute top-[40px] right-[60px] z-10 text-white/70 hover:text-white\"\n />\n ) : null}\n {right}\n </div>\n </div>\n);\n\nexport default SplitScreenLayout;\n"],"names":["CloseButton","onClose","className","jsx","DismissIcon","SplitScreenLayout","left","right","logo","footer","jsxs"],"mappings":";;AAWA,MAAMA,IAAqE,CAAC;AAAA,EACxE,SAAAC;AAAA,EACA,WAAAC,IAAY;AAChB,MACI,gBAAAC;AAAA,EAAC;AAAA,EAAA;AAAA,IACG,MAAK;AAAA,IACL,cAAW;AAAA,IACX,SAASF;AAAA,IACT,WAAW,iGAAiGC,CAAS;AAAA,IAErH,UAAA,gBAAAC,EAACC,GAAA,EAAY,MAAM,GAAA,CAAI;AAAA,EAAA;AAC3B,GAGEC,IAAsD,CAAC;AAAA,EACzD,MAAAC;AAAA,EACA,OAAAC;AAAA,EACA,MAAAC;AAAA,EACA,SAAAP;AAAA,EACA,QAAAQ;AACJ,MACI,gBAAAC,EAAC,OAAA,EAAI,WAAU,+FACX,UAAA;AAAA,EAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,iEACX,UAAA;AAAA,IAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,yDACX,UAAA;AAAA,MAAA,gBAAAP,EAAC,QAAA,EAAK,WAAU,mBAAkB,eAAW,IAAC;AAAA,MAC7CK,KAAQ,gBAAAL,EAAC,QAAA,EAAK,WAAU,mBAAkB,eAAW,IAAC;AAAA,MACvD,gBAAAA,EAAC,QAAA,EAAK,WAAU,oDACX,UAAAF,IACG,gBAAAE;AAAA,QAACH;AAAA,QAAA;AAAA,UACG,SAAAC;AAAA,UACA,WAAU;AAAA,QAAA;AAAA,MAAA,IAEd,KAAA,CACR;AAAA,IAAA,GACJ;AAAA,IACA,gBAAAE,EAAC,SAAI,WAAU,4GACX,4BAAC,OAAA,EAAI,WAAU,gEACV,UAAAG,EAAA,CACL,EAAA,CACJ;AAAA,IACA,gBAAAH,EAAC,OAAA,EAAI,WAAU,iDAAiD,UAAAM,EAAA,CAAO;AAAA,EAAA,GAC3E;AAAA,EACA,gBAAAC,EAAC,OAAA,EAAI,WAAU,+IACV,UAAA;AAAA,IAAAT,IACG,gBAAAE;AAAA,MAACH;AAAA,MAAA;AAAA,QACG,SAAAC;AAAA,QACA,WAAU;AAAA,MAAA;AAAA,IAAA,IAEd;AAAA,IACHM;AAAA,EAAA,EAAA,CACL;AAAA,EAAA,CACJ;"}
1
+ {"version":3,"file":"SplitScreenLayout.js","sources":["../../src/components/SplitScreenLayout.tsx"],"sourcesContent":["import React from 'react';\nimport CloseButton from './CloseButton';\n\ninterface SplitScreenLayoutProps {\n left: React.ReactNode;\n right: React.ReactNode;\n logo?: React.ReactNode;\n onClose?: () => void;\n footer?: React.ReactNode;\n}\n\nconst SplitScreenLayout: React.FC<SplitScreenLayoutProps> = ({\n left,\n right,\n logo,\n onClose,\n footer,\n}) => (\n <div className=\"flex h-screen w-full overflow-hidden gap-3 bg-[var(--malachite-card-sunken-background)] p-3\">\n <div className=\"flex min-w-0 flex-1 flex-col items-center px-[60px] py-[40px]\">\n <div className=\"flex h-6 w-full shrink-0 items-center justify-between\">\n <span className=\"size-6 shrink-0\" aria-hidden />\n {logo ?? <span className=\"size-6 shrink-0\" aria-hidden />}\n <span className=\"flex size-6 shrink-0 items-center justify-center\">\n {onClose ? <CloseButton onClick={onClose} className=\"lg:hidden\" /> : null}\n </span>\n </div>\n <div className=\"flex w-full min-h-0 flex-1 flex-col overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\">\n <div className=\"m-auto flex w-full max-w-[380px] flex-col items-center py-10\">\n {left}\n </div>\n </div>\n <div className=\"flex h-6 shrink-0 items-center justify-center\">{footer}</div>\n </div>\n <div className=\"relative hidden min-w-0 flex-1 flex-col items-center justify-center overflow-hidden rounded-[28px] bg-[#2b2b2b] px-[60px] py-[40px] lg:flex\">\n {/* 6px in from 40/60: the 24px target is 12px wider than the glyph, which stays put. */}\n {onClose ? (\n <CloseButton\n onClick={onClose}\n surface=\"inverse\"\n className=\"absolute top-[34px] right-[54px] z-10\"\n />\n ) : null}\n {right}\n </div>\n </div>\n);\n\nexport default SplitScreenLayout;\n"],"names":["SplitScreenLayout","left","right","logo","onClose","footer","jsxs","jsx","CloseButton"],"mappings":";;AAWA,MAAMA,IAAsD,CAAC;AAAA,EACzD,MAAAC;AAAA,EACA,OAAAC;AAAA,EACA,MAAAC;AAAA,EACA,SAAAC;AAAA,EACA,QAAAC;AACJ,MACI,gBAAAC,EAAC,OAAA,EAAI,WAAU,+FACX,UAAA;AAAA,EAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,iEACX,UAAA;AAAA,IAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,yDACX,UAAA;AAAA,MAAA,gBAAAC,EAAC,QAAA,EAAK,WAAU,mBAAkB,eAAW,IAAC;AAAA,MAC7CJ,KAAQ,gBAAAI,EAAC,QAAA,EAAK,WAAU,mBAAkB,eAAW,IAAC;AAAA,MACvD,gBAAAA,EAAC,QAAA,EAAK,WAAU,oDACX,UAAAH,IAAU,gBAAAG,EAACC,GAAA,EAAY,SAASJ,GAAS,WAAU,YAAA,CAAY,IAAK,KAAA,CACzE;AAAA,IAAA,GACJ;AAAA,IACA,gBAAAG,EAAC,SAAI,WAAU,4GACX,4BAAC,OAAA,EAAI,WAAU,gEACV,UAAAN,EAAA,CACL,EAAA,CACJ;AAAA,IACA,gBAAAM,EAAC,OAAA,EAAI,WAAU,iDAAiD,UAAAF,EAAA,CAAO;AAAA,EAAA,GAC3E;AAAA,EACA,gBAAAC,EAAC,OAAA,EAAI,WAAU,+IAEV,UAAA;AAAA,IAAAF,IACG,gBAAAG;AAAA,MAACC;AAAA,MAAA;AAAA,QACG,SAASJ;AAAA,QACT,SAAQ;AAAA,QACR,WAAU;AAAA,MAAA;AAAA,IAAA,IAEd;AAAA,IACHF;AAAA,EAAA,EAAA,CACL;AAAA,EAAA,CACJ;"}
@@ -0,0 +1,158 @@
1
+ import { jsx as f, jsxs as P } from "react/jsx-runtime";
2
+ import D, { useRef as w, useState as H, useEffect as R } from "react";
3
+ import U from "../Tooltip.js";
4
+ import { DEFAULT_MIN_CHARS as L, fitPath as q } from "./pathTextFit.js";
5
+ import { observePath as K, scheduleRemeasure as _, measurerFor as j, fontOf as V } from "./pathTextMeasure.js";
6
+ import { getFontSize as Y, getFontWeight as Z } from "./textStyles.js";
7
+ const k = {
8
+ display: "grid",
9
+ gridTemplateColumns: "minmax(0, 1fr)",
10
+ minWidth: 0,
11
+ maxWidth: "100%"
12
+ }, $ = {
13
+ gridArea: "1 / 1",
14
+ height: 0,
15
+ overflow: "hidden",
16
+ visibility: "hidden",
17
+ whiteSpace: "pre"
18
+ }, z = {
19
+ gridArea: "1 / 1",
20
+ display: "flex",
21
+ minWidth: 0,
22
+ overflow: "hidden",
23
+ whiteSpace: "nowrap"
24
+ }, B = {
25
+ position: "absolute",
26
+ width: 1,
27
+ height: 1,
28
+ margin: -1,
29
+ overflow: "hidden",
30
+ clipPath: "inset(50%)",
31
+ whiteSpace: "nowrap"
32
+ }, v = {
33
+ default: {},
34
+ secondary: {
35
+ color: "var(--malachite-text-secondary, rgb(123, 123, 123))",
36
+ fontWeight: Z("normal")
37
+ }
38
+ }, G = { ...v.secondary, flexShrink: 0, whiteSpace: "pre" }, p = (t) => t.tone ?? "default", J = (t, r) => t !== null && t.width === r.width && t.fonts.default === r.fonts.default && t.fonts.secondary === r.fonts.secondary, Q = (t, r, d) => {
39
+ if (!t || t.width <= 0)
40
+ return null;
41
+ const m = [];
42
+ for (const h of r) {
43
+ const y = j(t.fonts[p(h)] ?? "");
44
+ if (!y)
45
+ return null;
46
+ m.push({
47
+ text: h.text,
48
+ minChars: h.minChars ?? L,
49
+ measure: y
50
+ });
51
+ }
52
+ const c = j(t.fonts.secondary ?? "");
53
+ return r.length > 1 && !c ? null : q({
54
+ available: t.width,
55
+ segments: m,
56
+ separatorWidth: (c == null ? void 0 : c(d)) ?? 0
57
+ });
58
+ }, st = ({
59
+ segments: t,
60
+ separator: r = " / ",
61
+ size: d,
62
+ tooltip: m,
63
+ tooltipPlacement: c = "top",
64
+ tooltipSuppressed: h = !1,
65
+ className: y
66
+ }) => {
67
+ const u = w(null), A = w(null), T = w(null), [a, M] = H(null), S = w(null), W = t.length === 0;
68
+ R(() => {
69
+ const e = u.current;
70
+ if (e)
71
+ return K(e, (n, o) => {
72
+ const i = A.current;
73
+ if (!i)
74
+ return;
75
+ if (o || !S.current) {
76
+ const l = {};
77
+ for (const I of i.querySelectorAll("[data-tone]")) {
78
+ const g = I.dataset.tone;
79
+ l[g] ?? (l[g] = V(I));
80
+ }
81
+ S.current = l;
82
+ }
83
+ const F = { width: n, fonts: S.current };
84
+ M((l) => J(l, F) ? l : F);
85
+ });
86
+ }, [W]);
87
+ const C = new Set(t.map(p));
88
+ t.length > 1 && C.add("secondary");
89
+ const O = [...C].sort().join(" ");
90
+ if (R(() => {
91
+ const e = u.current, n = O.split(" ").some((o) => !(a != null && a.fonts[o]));
92
+ e && a && n && _(e);
93
+ }, [O, a]), R(() => {
94
+ u.current && _(u.current);
95
+ }, [d]), W)
96
+ return null;
97
+ const s = Q(a, t, r), x = t.length - 1, E = t.map((e) => e.text).join(r), N = (e, n) => {
98
+ const o = e.minChars ?? L, i = !s && n < x && e.text.length > o + 1;
99
+ return {
100
+ ...v[p(e)],
101
+ minWidth: i ? `${o + 1}ch` : 0,
102
+ overflow: "hidden",
103
+ // Fitted text already carries its ellipsis; a sub-pixel overflow must not add one.
104
+ textOverflow: s ? "clip" : "ellipsis",
105
+ flexShrink: n < x ? 1e3 * (x - n) : 1
106
+ };
107
+ }, b = (e, n) => t.map((o, i) => (
108
+ // Positional: an index is a level of the path, and texts repeat ("Acme / Acme").
109
+ /* @__PURE__ */ P(D.Fragment, { children: [
110
+ i > 0 && /* @__PURE__ */ f("span", { "data-tone": n ? "secondary" : void 0, style: G, children: r }),
111
+ /* @__PURE__ */ f(
112
+ "span",
113
+ {
114
+ "data-tone": n ? p(o) : void 0,
115
+ style: n ? v[p(o)] : N(o, i),
116
+ children: e[i]
117
+ }
118
+ )
119
+ ] }, i)
120
+ ));
121
+ return /* @__PURE__ */ f(
122
+ U,
123
+ {
124
+ text: m ?? E,
125
+ placement: c,
126
+ triggerAs: "span",
127
+ describesTrigger: !1,
128
+ suppressed: h,
129
+ isShrinkable: !0,
130
+ shouldShow: () => {
131
+ if (s)
132
+ return s.truncated;
133
+ const e = T.current;
134
+ return !!e && [...e.children].some((n) => n.scrollWidth > n.clientWidth);
135
+ },
136
+ children: /* @__PURE__ */ P(
137
+ "span",
138
+ {
139
+ ref: u,
140
+ className: y,
141
+ style: d ? { ...k, fontSize: Y(d) } : k,
142
+ children: [
143
+ /* @__PURE__ */ f("span", { ref: A, "aria-hidden": "true", style: $, children: b(
144
+ t.map((e) => e.text),
145
+ !0
146
+ ) }),
147
+ /* @__PURE__ */ f("span", { ref: T, "aria-hidden": "true", style: z, children: b((s == null ? void 0 : s.texts) ?? t.map((e) => e.text), !1) }),
148
+ /* @__PURE__ */ f("span", { style: B, children: E })
149
+ ]
150
+ }
151
+ )
152
+ }
153
+ );
154
+ };
155
+ export {
156
+ st as default
157
+ };
158
+ //# sourceMappingURL=PathText.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PathText.js","sources":["../../../src/components/Typography/PathText.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport Tooltip from '../Tooltip';\nimport { DEFAULT_MIN_CHARS, type FitSegment, type PathFit, fitPath } from './pathTextFit';\nimport { fontOf, measurerFor, observePath, scheduleRemeasure } from './pathTextMeasure';\nimport { type FontSize, getFontSize, getFontWeight } from './textStyles';\n\n/** `default` takes the surrounding colour and weight; `secondary` recedes, as an ancestor should. */\nexport type PathTextTone = 'default' | 'secondary';\n\nexport interface PathTextSegment {\n text: string;\n tone?: PathTextTone;\n /** Kept when cut, before the next segment gives way. Defaults to 2; ignored on the last. */\n minChars?: number;\n}\n\nexport interface PathTextProps {\n /** Root first, leaf last; segments give way in that order, so the leaf stays whole longest. */\n segments: PathTextSegment[];\n /** Drawn between segments, never cut. Defaults to `' / '`. */\n separator?: string;\n /** Inherits the surrounding size when unset. */\n size?: FontSize;\n /** Shown when anything is cut. Defaults to the whole path; pass a node to wrap it. */\n tooltip?: React.ReactNode;\n tooltipPlacement?: 'top' | 'bottom' | 'left' | 'right';\n /** Holds the tooltip back, e.g. while the menu this path labels is open. */\n tooltipSuppressed?: boolean;\n className?: string;\n}\n\ninterface Measured {\n width: number;\n fonts: Partial<Record<PathTextTone, string>>;\n}\n\n// Inline styles, not classes: a path can sit in a menu portaled out of the shadow root.\n// SIZER keeps the uncut width, or a cut path would shrink its own box and be cut again.\nconst ROOT: React.CSSProperties = {\n display: 'grid',\n gridTemplateColumns: 'minmax(0, 1fr)',\n minWidth: 0,\n maxWidth: '100%',\n};\nconst SIZER: React.CSSProperties = {\n gridArea: '1 / 1',\n height: 0,\n overflow: 'hidden',\n visibility: 'hidden',\n whiteSpace: 'pre',\n};\nconst LINE: React.CSSProperties = {\n gridArea: '1 / 1',\n display: 'flex',\n minWidth: 0,\n overflow: 'hidden',\n whiteSpace: 'nowrap',\n};\nconst VISUALLY_HIDDEN: React.CSSProperties = {\n position: 'absolute',\n width: 1,\n height: 1,\n margin: -1,\n overflow: 'hidden',\n clipPath: 'inset(50%)',\n whiteSpace: 'nowrap',\n};\nconst TONE: Record<PathTextTone, React.CSSProperties> = {\n default: {},\n secondary: {\n color: 'var(--malachite-text-secondary, rgb(123, 123, 123))',\n fontWeight: getFontWeight('normal'),\n },\n};\nconst SEPARATOR: React.CSSProperties = { ...TONE.secondary, flexShrink: 0, whiteSpace: 'pre' };\n\nconst toneOf = (segment: PathTextSegment): PathTextTone => segment.tone ?? 'default';\n\nconst sameMeasured = (a: Measured | null, b: Measured): boolean =>\n a !== null &&\n a.width === b.width &&\n a.fonts.default === b.fonts.default &&\n a.fonts.secondary === b.fonts.secondary;\n\nconst fitOf = (\n measured: Measured | null,\n segments: PathTextSegment[],\n separator: string,\n): PathFit | null => {\n if (!measured || measured.width <= 0) {\n return null;\n }\n const fitSegments: FitSegment[] = [];\n for (const segment of segments) {\n const measure = measurerFor(measured.fonts[toneOf(segment)] ?? '');\n if (!measure) {\n return null;\n }\n fitSegments.push({\n text: segment.text,\n minChars: segment.minChars ?? DEFAULT_MIN_CHARS,\n measure,\n });\n }\n const measureSeparator = measurerFor(measured.fonts.secondary ?? '');\n if (segments.length > 1 && !measureSeparator) {\n return null;\n }\n return fitPath({\n available: measured.width,\n segments: fitSegments,\n separatorWidth: measureSeparator?.(separator) ?? 0,\n });\n};\n\n/**\n * Canvas-measured, not `text-overflow`, which leaves a space before the ellipsis and cannot hold\n * a minimum length. Unmeasured (no canvas yet), `flex-shrink` gives the same priority.\n */\nconst PathText: React.FC<PathTextProps> = ({\n segments,\n separator = ' / ',\n size,\n tooltip,\n tooltipPlacement = 'top',\n tooltipSuppressed = false,\n className,\n}) => {\n const rootRef = useRef<HTMLSpanElement>(null);\n const sizerRef = useRef<HTMLSpanElement>(null);\n const lineRef = useRef<HTMLSpanElement>(null);\n const [measured, setMeasured] = useState<Measured | null>(null);\n // Fonts are read on first measure and on refresh only: a resize must not force a style read.\n const fontsRef = useRef<Measured['fonts'] | null>(null);\n const empty = segments.length === 0;\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: the root only exists once there are segments\n useEffect(() => {\n const root = rootRef.current;\n if (!root) {\n return;\n }\n return observePath(root, (width, refreshFonts) => {\n const sizer = sizerRef.current;\n if (!sizer) {\n return;\n }\n if (refreshFonts || !fontsRef.current) {\n const read: Measured['fonts'] = {};\n for (const node of sizer.querySelectorAll<HTMLElement>('[data-tone]')) {\n const tone = node.dataset.tone as PathTextTone;\n read[tone] ??= fontOf(node);\n }\n fontsRef.current = read;\n }\n const next = { width, fonts: fontsRef.current };\n setMeasured((previous) => (sameMeasured(previous, next) ? previous : next));\n });\n }, [empty]);\n\n const tones = new Set<PathTextTone>(segments.map(toneOf));\n if (segments.length > 1) {\n tones.add('secondary');\n }\n const toneKey = [...tones].sort().join(' ');\n useEffect(() => {\n const root = rootRef.current;\n const missing = toneKey.split(' ').some((tone) => !measured?.fonts[tone as PathTextTone]);\n if (root && measured && missing) {\n scheduleRemeasure(root);\n }\n }, [toneKey, measured]);\n\n // A new size keeps a cut path's box the same width, so the observer would not refire.\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-reads the fonts when `size` changes\n useEffect(() => {\n if (rootRef.current) {\n scheduleRemeasure(rootRef.current);\n }\n }, [size]);\n\n if (empty) {\n return null;\n }\n\n const fit = fitOf(measured, segments, separator);\n const last = segments.length - 1;\n const fullText = segments.map((segment) => segment.text).join(separator);\n\n const segmentStyle = (segment: PathTextSegment, index: number): React.CSSProperties => {\n const minChars = segment.minChars ?? DEFAULT_MIN_CHARS;\n const keepsMinimum = !fit && index < last && segment.text.length > minChars + 1;\n return {\n ...TONE[toneOf(segment)],\n minWidth: keepsMinimum ? `${minChars + 1}ch` : 0,\n overflow: 'hidden',\n // Fitted text already carries its ellipsis; a sub-pixel overflow must not add one.\n textOverflow: fit ? 'clip' : 'ellipsis',\n flexShrink: index < last ? 1000 * (last - index) : 1,\n };\n };\n\n const renderSegments = (texts: string[], forSizer: boolean) =>\n segments.map((segment, index) => (\n // Positional: an index is a level of the path, and texts repeat (\"Acme / Acme\").\n <React.Fragment key={index}>\n {index > 0 && (\n <span data-tone={forSizer ? 'secondary' : undefined} style={SEPARATOR}>\n {separator}\n </span>\n )}\n <span\n data-tone={forSizer ? toneOf(segment) : undefined}\n style={forSizer ? TONE[toneOf(segment)] : segmentStyle(segment, index)}\n >\n {texts[index]}\n </span>\n </React.Fragment>\n ));\n\n const isCut = (): boolean => {\n if (fit) {\n return fit.truncated;\n }\n const line = lineRef.current;\n return !!line && [...line.children].some((child) => child.scrollWidth > child.clientWidth);\n };\n\n return (\n <Tooltip\n text={tooltip ?? fullText}\n placement={tooltipPlacement}\n triggerAs=\"span\"\n describesTrigger={false}\n suppressed={tooltipSuppressed}\n isShrinkable\n shouldShow={isCut}\n >\n <span\n ref={rootRef}\n className={className}\n style={size ? { ...ROOT, fontSize: getFontSize(size) } : ROOT}\n >\n <span ref={sizerRef} aria-hidden=\"true\" style={SIZER}>\n {renderSegments(\n segments.map((segment) => segment.text),\n true,\n )}\n </span>\n <span ref={lineRef} aria-hidden=\"true\" style={LINE}>\n {renderSegments(fit?.texts ?? segments.map((segment) => segment.text), false)}\n </span>\n <span style={VISUALLY_HIDDEN}>{fullText}</span>\n </span>\n </Tooltip>\n );\n};\n\nexport default PathText;\n"],"names":["ROOT","SIZER","LINE","VISUALLY_HIDDEN","TONE","getFontWeight","SEPARATOR","toneOf","segment","sameMeasured","a","b","fitOf","measured","segments","separator","fitSegments","measure","measurerFor","DEFAULT_MIN_CHARS","measureSeparator","fitPath","PathText","size","tooltip","tooltipPlacement","tooltipSuppressed","className","rootRef","useRef","sizerRef","lineRef","setMeasured","useState","fontsRef","empty","useEffect","root","observePath","width","refreshFonts","sizer","read","node","tone","fontOf","next","previous","tones","toneKey","missing","scheduleRemeasure","fit","last","fullText","segmentStyle","index","minChars","keepsMinimum","renderSegments","texts","forSizer","jsxs","React","jsx","Tooltip","line","child","getFontSize"],"mappings":"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"}
@@ -4,28 +4,30 @@ import r from "./FormSectionTitle.js";
4
4
  import m from "./LinkText.js";
5
5
  import i from "./MarkdownBlock.js";
6
6
  import e from "./PageTitle.js";
7
- import p from "./SecondaryText.js";
8
- import f from "./SectionTitle.js";
9
- import a from "./StatisticLabel.js";
10
- import T from "./StatisticValue.js";
11
- import l from "./TerminalText.js";
7
+ import p from "./PathText.js";
8
+ import f from "./SecondaryText.js";
9
+ import a from "./SectionTitle.js";
10
+ import T from "./StatisticLabel.js";
11
+ import l from "./StatisticValue.js";
12
+ import x from "./TerminalText.js";
12
13
  import c from "./Text.js";
13
- const w = {
14
+ const B = {
14
15
  CodeText: o,
15
16
  EllipsisText: t,
16
17
  FormSectionTitle: r,
17
18
  LinkText: m,
18
19
  MarkdownBlock: i,
19
20
  PageTitle: e,
20
- SecondaryText: p,
21
- SectionTitle: f,
22
- StatisticLabel: a,
23
- StatisticValue: T,
24
- TerminalText: l,
21
+ PathText: p,
22
+ SecondaryText: f,
23
+ SectionTitle: a,
24
+ StatisticLabel: T,
25
+ StatisticValue: l,
26
+ TerminalText: x,
25
27
  Text: c
26
28
  };
27
29
  export {
28
- w as Typography,
29
- w as default
30
+ B as Typography,
31
+ B as default
30
32
  };
31
33
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/components/Typography/index.tsx"],"sourcesContent":["import { default as CodeText } from './CodeText';\nimport { default as EllipsisText } from './EllipsisText';\nimport { default as FormSectionTitle } from './FormSectionTitle';\nimport { default as LinkText } from './LinkText';\nimport { default as MarkdownBlock } from './MarkdownBlock';\nimport { default as PageTitle } from './PageTitle';\nimport { default as SecondaryText } from './SecondaryText';\nimport { default as SectionTitle } from './SectionTitle';\nimport { default as StatisticLabel } from './StatisticLabel';\nimport { default as StatisticValue } from './StatisticValue';\nimport { default as TerminalText } from './TerminalText';\nimport { default as Text } from './Text';\n\nexport type { CodeTextProps, CodeTextVariant } from './CodeText';\nexport type { MarkdownLinks, MarkdownProps, MarkdownVariant } from './MarkdownBlock';\nexport type { FontSize, FontWeight, TextColor, TextElement, TextProps } from './Text';\n\nexport const Typography = {\n CodeText,\n EllipsisText,\n FormSectionTitle,\n LinkText,\n MarkdownBlock,\n PageTitle,\n SecondaryText,\n SectionTitle,\n StatisticLabel,\n StatisticValue,\n TerminalText,\n Text,\n};\n\nexport default Typography;\n"],"names":["Typography","CodeText","EllipsisText","FormSectionTitle","LinkText","MarkdownBlock","PageTitle","SecondaryText","SectionTitle","StatisticLabel","StatisticValue","TerminalText","Text"],"mappings":";;;;;;;;;;;;AAiBO,MAAMA,IAAa;AAAA,EACtB,UAAAC;AAAA,EACA,cAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,UAAAC;AAAA,EACA,eAAAC;AAAA,EACA,WAAAC;AAAA,EACA,eAAAC;AAAA,EACA,cAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,cAAAC;AAAA,EACA,MAAAC;AACJ;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/components/Typography/index.tsx"],"sourcesContent":["import { default as CodeText } from './CodeText';\nimport { default as EllipsisText } from './EllipsisText';\nimport { default as FormSectionTitle } from './FormSectionTitle';\nimport { default as LinkText } from './LinkText';\nimport { default as MarkdownBlock } from './MarkdownBlock';\nimport { default as PageTitle } from './PageTitle';\nimport { default as PathText } from './PathText';\nimport { default as SecondaryText } from './SecondaryText';\nimport { default as SectionTitle } from './SectionTitle';\nimport { default as StatisticLabel } from './StatisticLabel';\nimport { default as StatisticValue } from './StatisticValue';\nimport { default as TerminalText } from './TerminalText';\nimport { default as Text } from './Text';\n\nexport type { CodeTextProps, CodeTextVariant } from './CodeText';\nexport type { MarkdownLinks, MarkdownProps, MarkdownVariant } from './MarkdownBlock';\nexport type { PathTextProps, PathTextSegment, PathTextTone } from './PathText';\nexport type { FontSize, FontWeight, TextColor, TextElement, TextProps } from './Text';\n\nexport const Typography = {\n CodeText,\n EllipsisText,\n FormSectionTitle,\n LinkText,\n MarkdownBlock,\n PageTitle,\n PathText,\n SecondaryText,\n SectionTitle,\n StatisticLabel,\n StatisticValue,\n TerminalText,\n Text,\n};\n\nexport default Typography;\n"],"names":["Typography","CodeText","EllipsisText","FormSectionTitle","LinkText","MarkdownBlock","PageTitle","PathText","SecondaryText","SectionTitle","StatisticLabel","StatisticValue","TerminalText","Text"],"mappings":";;;;;;;;;;;;;AAmBO,MAAMA,IAAa;AAAA,EACtB,UAAAC;AAAA,EACA,cAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,UAAAC;AAAA,EACA,eAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,eAAAC;AAAA,EACA,cAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,cAAAC;AAAA,EACA,MAAAC;AACJ;"}
@@ -0,0 +1,42 @@
1
+ const p = "…", A = 2;
2
+ let a;
3
+ const S = (e) => {
4
+ if (a === void 0) {
5
+ const t = typeof Intl > "u" ? void 0 : Intl.Segmenter;
6
+ a = t ? new t(void 0, { granularity: "grapheme" }) : null;
7
+ }
8
+ return a ? Array.from(a.segment(e), (t) => t.segment) : Array.from(e);
9
+ }, f = (e, t) => `${e.slice(0, t).join("").trimEnd()}…`, L = (e, t, s) => {
10
+ if (s(e) <= t + 0.25)
11
+ return e;
12
+ const o = S(e);
13
+ let r = 0, h = o.length - 1;
14
+ for (; r < h; ) {
15
+ const c = Math.ceil((r + h) / 2);
16
+ s(f(o, c)) <= t ? r = c : h = c - 1;
17
+ }
18
+ return r === 0 ? "…" : f(o, r);
19
+ }, I = ({ text: e, minChars: t }) => {
20
+ const s = S(e);
21
+ return s.length > t + 1 ? f(s, t) : e;
22
+ }, P = ({ available: e, segments: t, separatorWidth: s }) => {
23
+ const o = t.map((n) => n.text), r = t.map((n) => n.measure(n.text)), h = s * Math.max(t.length - 1, 0), c = (n) => n.reduce((u, l) => u + l, h);
24
+ if (c(r) <= e + 0.25)
25
+ return { texts: o, truncated: !1 };
26
+ const i = t.length - 1;
27
+ for (let n = 0; n < i; n += 1) {
28
+ const u = t[n], l = I(u), m = u.measure(l), E = c(r) - r[n];
29
+ if (E + m <= e)
30
+ return o[n] = L(u.text, e - E, u.measure), { texts: o, truncated: !0 };
31
+ o[n] = l, r[n] = m;
32
+ }
33
+ const d = t[i], g = e - (c(r) - r[i]);
34
+ return o[i] = L(d.text, g, d.measure), { texts: o, truncated: !0 };
35
+ };
36
+ export {
37
+ A as DEFAULT_MIN_CHARS,
38
+ p as ELLIPSIS,
39
+ P as fitPath,
40
+ L as fitText
41
+ };
42
+ //# sourceMappingURL=pathTextFit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pathTextFit.js","sources":["../../../src/components/Typography/pathTextFit.ts"],"sourcesContent":["export type MeasureText = (text: string) => number;\n\nexport interface FitSegment {\n text: string;\n minChars: number;\n measure: MeasureText;\n}\n\nexport interface PathFitInput {\n available: number;\n segments: FitSegment[];\n separatorWidth: number;\n}\n\nexport interface PathFit {\n texts: string[];\n truncated: boolean;\n}\n\nexport const ELLIPSIS = '…';\nexport const DEFAULT_MIN_CHARS = 2;\n\n// Canvas and layout widths differ by rounding; a path that exactly fills its box stays whole.\nconst TOLERANCE_PX = 0.25;\n\n// Declared locally: the configured lib predates `Intl.Segmenter`, which every target browser has.\ninterface GraphemeSegmenter {\n segment(text: string): Iterable<{ segment: string }>;\n}\ntype SegmenterConstructor = new (\n locale: undefined,\n options: { granularity: 'grapheme' },\n) => GraphemeSegmenter;\n\nlet segmenter: GraphemeSegmenter | null | undefined;\n\nconst graphemesOf = (text: string): string[] => {\n if (segmenter === undefined) {\n const Segmenter =\n typeof Intl === 'undefined'\n ? undefined\n : (Intl as unknown as { Segmenter?: SegmenterConstructor }).Segmenter;\n segmenter = Segmenter ? new Segmenter(undefined, { granularity: 'grapheme' }) : null;\n }\n return segmenter\n ? Array.from(segmenter.segment(text), (part) => part.segment)\n : Array.from(text);\n};\n\nconst cut = (chars: string[], length: number): string =>\n `${chars.slice(0, length).join('').trimEnd()}${ELLIPSIS}`;\n\nexport const fitText = (text: string, width: number, measure: MeasureText): string => {\n if (measure(text) <= width + TOLERANCE_PX) {\n return text;\n }\n const chars = graphemesOf(text);\n let low = 0;\n let high = chars.length - 1;\n while (low < high) {\n const middle = Math.ceil((low + high) / 2);\n if (measure(cut(chars, middle)) <= width) {\n low = middle;\n } else {\n high = middle - 1;\n }\n }\n return low === 0 ? ELLIPSIS : cut(chars, low);\n};\n\n// `+ 1`: an ellipsis is about as wide as the one character it would replace.\nconst shortestOf = ({ text, minChars }: FitSegment): string => {\n const chars = graphemesOf(text);\n return chars.length > minChars + 1 ? cut(chars, minChars) : text;\n};\n\nexport const fitPath = ({ available, segments, separatorWidth }: PathFitInput): PathFit => {\n const texts = segments.map((segment) => segment.text);\n const widths = segments.map((segment) => segment.measure(segment.text));\n const separators = separatorWidth * Math.max(segments.length - 1, 0);\n const total = (values: number[]) => values.reduce((sum, value) => sum + value, separators);\n\n if (total(widths) <= available + TOLERANCE_PX) {\n return { texts, truncated: false };\n }\n\n const last = segments.length - 1;\n for (let index = 0; index < last; index += 1) {\n const segment = segments[index];\n const shortest = shortestOf(segment);\n const shortestWidth = segment.measure(shortest);\n const others = total(widths) - widths[index];\n if (others + shortestWidth <= available) {\n texts[index] = fitText(segment.text, available - others, segment.measure);\n return { texts, truncated: true };\n }\n texts[index] = shortest;\n widths[index] = shortestWidth;\n }\n\n const lastSegment = segments[last];\n const room = available - (total(widths) - widths[last]);\n texts[last] = fitText(lastSegment.text, room, lastSegment.measure);\n return { texts, truncated: true };\n};\n"],"names":["ELLIPSIS","DEFAULT_MIN_CHARS","segmenter","graphemesOf","text","Segmenter","part","cut","chars","length","fitText","width","measure","low","high","middle","shortestOf","minChars","fitPath","available","segments","separatorWidth","texts","segment","widths","separators","total","values","sum","value","last","index","shortest","shortestWidth","others","lastSegment","room"],"mappings":"AAmBO,MAAMA,IAAW,KACXC,IAAoB;AAcjC,IAAIC;AAEJ,MAAMC,IAAc,CAACC,MAA2B;AAC5C,MAAIF,MAAc,QAAW;AACzB,UAAMG,IACF,OAAO,OAAS,MACV,SACC,KAAyD;AACpE,IAAAH,IAAYG,IAAY,IAAIA,EAAU,QAAW,EAAE,aAAa,WAAA,CAAY,IAAI;AAAA,EACpF;AACA,SAAOH,IACD,MAAM,KAAKA,EAAU,QAAQE,CAAI,GAAG,CAACE,MAASA,EAAK,OAAO,IAC1D,MAAM,KAAKF,CAAI;AACzB,GAEMG,IAAM,CAACC,GAAiBC,MAC1B,GAAGD,EAAM,MAAM,GAAGC,CAAM,EAAE,KAAK,EAAE,EAAE,QAAA,CAAS,KAEnCC,IAAU,CAACN,GAAcO,GAAeC,MAAiC;AAClF,MAAIA,EAAQR,CAAI,KAAKO,IAAQ;AACzB,WAAOP;AAEX,QAAMI,IAAQL,EAAYC,CAAI;AAC9B,MAAIS,IAAM,GACNC,IAAON,EAAM,SAAS;AAC1B,SAAOK,IAAMC,KAAM;AACf,UAAMC,IAAS,KAAK,MAAMF,IAAMC,KAAQ,CAAC;AACzC,IAAIF,EAAQL,EAAIC,GAAOO,CAAM,CAAC,KAAKJ,IAC/BE,IAAME,IAEND,IAAOC,IAAS;AAAA,EAExB;AACA,SAAOF,MAAQ,IAAI,MAAWN,EAAIC,GAAOK,CAAG;AAChD,GAGMG,IAAa,CAAC,EAAE,MAAAZ,GAAM,UAAAa,QAAmC;AAC3D,QAAMT,IAAQL,EAAYC,CAAI;AAC9B,SAAOI,EAAM,SAASS,IAAW,IAAIV,EAAIC,GAAOS,CAAQ,IAAIb;AAChE,GAEac,IAAU,CAAC,EAAE,WAAAC,GAAW,UAAAC,GAAU,gBAAAC,QAA4C;AACvF,QAAMC,IAAQF,EAAS,IAAI,CAACG,MAAYA,EAAQ,IAAI,GAC9CC,IAASJ,EAAS,IAAI,CAACG,MAAYA,EAAQ,QAAQA,EAAQ,IAAI,CAAC,GAChEE,IAAaJ,IAAiB,KAAK,IAAID,EAAS,SAAS,GAAG,CAAC,GAC7DM,IAAQ,CAACC,MAAqBA,EAAO,OAAO,CAACC,GAAKC,MAAUD,IAAMC,GAAOJ,CAAU;AAEzF,MAAIC,EAAMF,CAAM,KAAKL,IAAY;AAC7B,WAAO,EAAE,OAAAG,GAAO,WAAW,GAAA;AAG/B,QAAMQ,IAAOV,EAAS,SAAS;AAC/B,WAASW,IAAQ,GAAGA,IAAQD,GAAMC,KAAS,GAAG;AAC1C,UAAMR,IAAUH,EAASW,CAAK,GACxBC,IAAWhB,EAAWO,CAAO,GAC7BU,IAAgBV,EAAQ,QAAQS,CAAQ,GACxCE,IAASR,EAAMF,CAAM,IAAIA,EAAOO,CAAK;AAC3C,QAAIG,IAASD,KAAiBd;AAC1B,aAAAG,EAAMS,CAAK,IAAIrB,EAAQa,EAAQ,MAAMJ,IAAYe,GAAQX,EAAQ,OAAO,GACjE,EAAE,OAAAD,GAAO,WAAW,GAAA;AAE/B,IAAAA,EAAMS,CAAK,IAAIC,GACfR,EAAOO,CAAK,IAAIE;AAAA,EACpB;AAEA,QAAME,IAAcf,EAASU,CAAI,GAC3BM,IAAOjB,KAAaO,EAAMF,CAAM,IAAIA,EAAOM,CAAI;AACrD,SAAAR,EAAMQ,CAAI,IAAIpB,EAAQyB,EAAY,MAAMC,GAAMD,EAAY,OAAO,GAC1D,EAAE,OAAAb,GAAO,WAAW,GAAA;AAC/B;"}
@@ -0,0 +1,67 @@
1
+ const s = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Set();
2
+ let c = null, i = null, h = !1, a;
3
+ const l = /* @__PURE__ */ new Map(), S = 2e3, F = () => {
4
+ if (a === void 0)
5
+ try {
6
+ a = document.createElement("canvas").getContext("2d");
7
+ } catch {
8
+ a = null;
9
+ }
10
+ return a;
11
+ }, w = "", R = (e) => {
12
+ const t = F();
13
+ if (!t || !e)
14
+ return null;
15
+ const [n, o = "0px"] = e.split(w);
16
+ return (r) => {
17
+ const u = `${e}\0${r}`, g = l.get(u);
18
+ if (g !== void 0)
19
+ return g;
20
+ t.font !== n && (t.font = n), "letterSpacing" in t && t.letterSpacing !== o && (t.letterSpacing = o);
21
+ const m = t.measureText(r).width;
22
+ return l.size >= S && l.clear(), l.set(u, m), m;
23
+ };
24
+ }, z = (e) => {
25
+ const t = getComputedStyle(e), n = t.font || `${t.fontStyle} ${t.fontWeight} ${t.fontSize} ${t.fontFamily}`.trim(), o = t.letterSpacing === "normal" ? "0px" : t.letterSpacing;
26
+ return `${n}${w}${o}`;
27
+ }, $ = () => {
28
+ i = null;
29
+ const e = [...f];
30
+ f.clear();
31
+ for (const t of e) {
32
+ const n = s.get(t);
33
+ n && n.width !== null && n.remeasure(n.width, !0);
34
+ }
35
+ }, y = (e) => {
36
+ f.add(e), i === null && (i = requestAnimationFrame($));
37
+ }, p = () => {
38
+ var e;
39
+ l.clear();
40
+ for (const t of s.keys())
41
+ y(t);
42
+ ((e = document.fonts) == null ? void 0 : e.status) === "loaded" && d(!1);
43
+ };
44
+ function d(e) {
45
+ var t, n, o, r;
46
+ e !== h && (h = e, e ? (n = (t = document.fonts) == null ? void 0 : t.addEventListener) == null || n.call(t, "loadingdone", p) : (r = (o = document.fonts) == null ? void 0 : o.removeEventListener) == null || r.call(o, "loadingdone", p));
47
+ }
48
+ const v = () => {
49
+ var e;
50
+ ((e = document.fonts) == null ? void 0 : e.status) === "loading" && d(!0);
51
+ }, x = (e) => {
52
+ v();
53
+ for (const { target: t, contentRect: n } of e) {
54
+ const o = s.get(t);
55
+ o && (o.width = n.width, o.remeasure(n.width, !1));
56
+ }
57
+ }, C = (e, t) => typeof ResizeObserver > "u" ? () => {
58
+ } : (v(), c ?? (c = new ResizeObserver(x)), s.set(e, { remeasure: t, width: null }), c.observe(e), () => {
59
+ c == null || c.unobserve(e), s.delete(e), f.delete(e), s.size === 0 && (d(!1), i !== null && (cancelAnimationFrame(i), i = null));
60
+ });
61
+ export {
62
+ z as fontOf,
63
+ R as measurerFor,
64
+ C as observePath,
65
+ y as scheduleRemeasure
66
+ };
67
+ //# sourceMappingURL=pathTextMeasure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pathTextMeasure.js","sources":["../../../src/components/Typography/pathTextMeasure.ts"],"sourcesContent":["import type { MeasureText } from './pathTextFit';\n\ntype Remeasure = (width: number, refreshFonts: boolean) => void;\n\ninterface Entry {\n remeasure: Remeasure;\n width: number | null;\n}\n\nconst entries = new Map<Element, Entry>();\nconst pending = new Set<Element>();\nlet observer: ResizeObserver | null = null;\nlet frame: number | null = null;\nlet listeningForFonts = false;\nlet context: CanvasRenderingContext2D | null | undefined;\nconst widthCache = new Map<string, number>();\nconst WIDTH_CACHE_LIMIT = 2000;\n\nconst getContext = (): CanvasRenderingContext2D | null => {\n if (context === undefined) {\n try {\n context = document.createElement('canvas').getContext('2d');\n } catch {\n context = null;\n }\n }\n return context;\n};\n\nconst KEY_SEPARATOR = '\\u0001';\n\nexport const measurerFor = (fontKey: string): MeasureText | null => {\n const ctx = getContext();\n if (!ctx || !fontKey) {\n return null;\n }\n const [font, letterSpacing = '0px'] = fontKey.split(KEY_SEPARATOR);\n return (text) => {\n const key = `${fontKey}\\u0000${text}`;\n const cached = widthCache.get(key);\n if (cached !== undefined) {\n return cached;\n }\n if (ctx.font !== font) {\n ctx.font = font;\n }\n // Canvas ignores inherited `tracking-*`; set it, or tracked text measures narrow.\n if ('letterSpacing' in ctx && ctx.letterSpacing !== letterSpacing) {\n ctx.letterSpacing = letterSpacing;\n }\n const width = ctx.measureText(text).width;\n if (widthCache.size >= WIDTH_CACHE_LIMIT) {\n widthCache.clear();\n }\n widthCache.set(key, width);\n return width;\n };\n};\n\nexport const fontOf = (element: Element): string => {\n const style = getComputedStyle(element);\n // `font` can come back empty for a shorthand some longhand cannot express.\n const font =\n style.font ||\n `${style.fontStyle} ${style.fontWeight} ${style.fontSize} ${style.fontFamily}`.trim();\n const spacing = style.letterSpacing === 'normal' ? '0px' : style.letterSpacing;\n return `${font}${KEY_SEPARATOR}${spacing}`;\n};\n\nconst flush = () => {\n frame = null;\n const batch = [...pending];\n pending.clear();\n for (const element of batch) {\n const entry = entries.get(element);\n if (entry && entry.width !== null) {\n entry.remeasure(entry.width, true);\n }\n }\n};\n\nexport const scheduleRemeasure = (element: Element): void => {\n pending.add(element);\n if (frame === null) {\n frame = requestAnimationFrame(flush);\n }\n};\n\n// A webfont landing changes widths without resizing boxes. Listened for only while a load is in\n// flight, so nothing global is attached at rest.\nconst onFontsLoaded = () => {\n widthCache.clear();\n for (const element of entries.keys()) {\n scheduleRemeasure(element);\n }\n if (document.fonts?.status === 'loaded') {\n listenForFonts(false);\n }\n};\n\nfunction listenForFonts(on: boolean): void {\n if (on === listeningForFonts) {\n return;\n }\n listeningForFonts = on;\n if (on) {\n document.fonts?.addEventListener?.('loadingdone', onFontsLoaded);\n } else {\n document.fonts?.removeEventListener?.('loadingdone', onFontsLoaded);\n }\n}\n\nconst listenWhileFontsLoad = () => {\n if (document.fonts?.status === 'loading') {\n listenForFonts(true);\n }\n};\n\n// One observer for every path: a mount batch is one callback, not one per instance.\nconst onResize: ResizeObserverCallback = (resized) => {\n listenWhileFontsLoad();\n for (const { target, contentRect } of resized) {\n const entry = entries.get(target);\n if (entry) {\n entry.width = contentRect.width;\n entry.remeasure(contentRect.width, false);\n }\n }\n};\n\nexport const observePath = (element: Element, remeasure: Remeasure): (() => void) => {\n if (typeof ResizeObserver === 'undefined') {\n return () => undefined;\n }\n listenWhileFontsLoad();\n observer ??= new ResizeObserver(onResize);\n entries.set(element, { remeasure, width: null });\n observer.observe(element);\n return () => {\n observer?.unobserve(element);\n entries.delete(element);\n pending.delete(element);\n if (entries.size === 0) {\n listenForFonts(false);\n if (frame !== null) {\n cancelAnimationFrame(frame);\n frame = null;\n }\n }\n };\n};\n"],"names":["entries","pending","observer","frame","listeningForFonts","context","widthCache","WIDTH_CACHE_LIMIT","getContext","KEY_SEPARATOR","measurerFor","fontKey","ctx","font","letterSpacing","text","key","cached","width","fontOf","element","style","spacing","flush","batch","entry","scheduleRemeasure","onFontsLoaded","_a","listenForFonts","on","_b","_c","_d","listenWhileFontsLoad","onResize","resized","target","contentRect","observePath","remeasure"],"mappings":"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"}
@@ -1,10 +1,11 @@
1
- const o = "outline-none focus-visible:outline-offset-2 focus-visible:[outline:2px_solid_var(--malachite-text-primary)]", e = "outline-none focus-visible:[outline:2px_solid_var(--malachite-text-primary)] focus-visible:[outline-offset:-2px]", i = "pointer-events-none absolute inset-0 z-[2] hidden group-focus-visible:block [outline:2px_solid_var(--malachite-text-primary)] [outline-offset:-2px]", t = "outline-none has-[[role=radio]:focus-visible]:outline-offset-2 has-[[role=radio]:focus-visible]:[outline:2px_solid_var(--malachite-text-primary)]", l = "outline-none focus-visible:outline-offset-2 focus-visible:[outline:2px_solid_var(--malachite-selection-bar-foreground)]", s = "outline-none focus-visible:[outline:2px_solid_var(--malachite-inline-confirm-foreground)] focus-visible:[outline-offset:-2px]";
1
+ const o = "outline-none focus-visible:outline-offset-2 focus-visible:[outline:2px_solid_var(--malachite-text-primary)]", e = "outline-none focus-visible:[outline:2px_solid_var(--malachite-text-primary)] focus-visible:[outline-offset:-2px]", i = "pointer-events-none absolute inset-0 z-[2] hidden group-focus-visible:block [outline:2px_solid_var(--malachite-text-primary)] [outline-offset:-2px]", t = "outline-none has-[[role=radio]:focus-visible]:outline-offset-2 has-[[role=radio]:focus-visible]:[outline:2px_solid_var(--malachite-text-primary)]", s = "outline-none focus-visible:outline-offset-2 focus-visible:[outline:2px_solid_var(--malachite-selection-bar-foreground)]", l = "outline-none focus-visible:[outline:2px_solid_var(--malachite-inline-confirm-foreground)] focus-visible:[outline-offset:-2px]", n = "outline-none focus-visible:outline-offset-2 focus-visible:[outline:2px_solid_var(--malachite-close-button-inverse-hover-foreground,rgb(255,255,255))]";
2
2
  export {
3
3
  o as FOCUS_RING,
4
- s as FOCUS_RING_INLINE_CONFIRM_INSET,
4
+ n as FOCUS_RING_CLOSE_BUTTON_INVERSE,
5
+ l as FOCUS_RING_INLINE_CONFIRM_INSET,
5
6
  e as FOCUS_RING_INSET,
6
7
  i as FOCUS_RING_INSET_OVERLAY,
7
- l as FOCUS_RING_SELECTION_BAR,
8
+ s as FOCUS_RING_SELECTION_BAR,
8
9
  t as FOCUS_RING_WITHIN_RADIO
9
10
  };
10
11
  //# sourceMappingURL=focusRing.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"focusRing.js","sources":["../../src/components/focusRing.ts"],"sourcesContent":["// Shared keyboard focus indicator for interactive components.\n\n/** Standard focus ring — sits 2px outside the control. */\nexport const FOCUS_RING =\n 'outline-none focus-visible:outline-offset-2 focus-visible:[outline:2px_solid_var(--malachite-text-primary)]';\n\n/**\n * Inset focus ring — drawn 2px inside the element edge. Use for full-bleed elements\n * inside an `overflow-hidden` container (e.g. Surface.Row) where an outset outline\n * would be clipped.\n */\nexport const FOCUS_RING_INSET =\n 'outline-none focus-visible:[outline:2px_solid_var(--malachite-text-primary)] focus-visible:[outline-offset:-2px]';\n\n/** `FOCUS_RING_INSET` drawn by an overlay while its `group` ancestor has keyboard focus. */\nexport const FOCUS_RING_INSET_OVERLAY =\n 'pointer-events-none absolute inset-0 z-[2] hidden group-focus-visible:block [outline:2px_solid_var(--malachite-text-primary)] [outline-offset:-2px]';\n\n/** Ring drawn on a wrapper when a control *inside* it takes focus. */\nexport const FOCUS_RING_WITHIN =\n 'outline-none has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:[outline:2px_solid_var(--malachite-text-primary)]';\n\n/**\n * As `FOCUS_RING_WITHIN`, for a card whose control is a `role=\"radio\"` beside its links:\n * `:has(:focus-visible)` would also match a focused link and draw a second ring.\n */\nexport const FOCUS_RING_WITHIN_RADIO =\n 'outline-none has-[[role=radio]:focus-visible]:outline-offset-2 has-[[role=radio]:focus-visible]:[outline:2px_solid_var(--malachite-text-primary)]';\n\n/** Ring for a control on the SelectionBar, where `FOCUS_RING` would be invisible. */\nexport const FOCUS_RING_SELECTION_BAR =\n 'outline-none focus-visible:outline-offset-2 focus-visible:[outline:2px_solid_var(--malachite-selection-bar-foreground)]';\n\n/** Inset ring for a control on the InlineConfirm chip. */\nexport const FOCUS_RING_INLINE_CONFIRM_INSET =\n 'outline-none focus-visible:[outline:2px_solid_var(--malachite-inline-confirm-foreground)] focus-visible:[outline-offset:-2px]';\n"],"names":["FOCUS_RING","FOCUS_RING_INSET","FOCUS_RING_INSET_OVERLAY","FOCUS_RING_WITHIN_RADIO","FOCUS_RING_SELECTION_BAR","FOCUS_RING_INLINE_CONFIRM_INSET"],"mappings":"AAGO,MAAMA,IACT,+GAOSC,IACT,oHAGSC,IACT,uJAUSC,IACT,qJAGSC,IACT,2HAGSC,IACT;"}
1
+ {"version":3,"file":"focusRing.js","sources":["../../src/components/focusRing.ts"],"sourcesContent":["// Shared keyboard focus indicator for interactive components.\n\n/** Standard focus ring — sits 2px outside the control. */\nexport const FOCUS_RING =\n 'outline-none focus-visible:outline-offset-2 focus-visible:[outline:2px_solid_var(--malachite-text-primary)]';\n\n/**\n * Inset focus ring — drawn 2px inside the element edge. Use for full-bleed elements\n * inside an `overflow-hidden` container (e.g. Surface.Row) where an outset outline\n * would be clipped.\n */\nexport const FOCUS_RING_INSET =\n 'outline-none focus-visible:[outline:2px_solid_var(--malachite-text-primary)] focus-visible:[outline-offset:-2px]';\n\n/** `FOCUS_RING_INSET` drawn by an overlay while its `group` ancestor has keyboard focus. */\nexport const FOCUS_RING_INSET_OVERLAY =\n 'pointer-events-none absolute inset-0 z-[2] hidden group-focus-visible:block [outline:2px_solid_var(--malachite-text-primary)] [outline-offset:-2px]';\n\n/** Ring drawn on a wrapper when a control *inside* it takes focus. */\nexport const FOCUS_RING_WITHIN =\n 'outline-none has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:[outline:2px_solid_var(--malachite-text-primary)]';\n\n/**\n * As `FOCUS_RING_WITHIN`, for a card whose control is a `role=\"radio\"` beside its links:\n * `:has(:focus-visible)` would also match a focused link and draw a second ring.\n */\nexport const FOCUS_RING_WITHIN_RADIO =\n 'outline-none has-[[role=radio]:focus-visible]:outline-offset-2 has-[[role=radio]:focus-visible]:[outline:2px_solid_var(--malachite-text-primary)]';\n\n/** Ring for a control on the SelectionBar, where `FOCUS_RING` would be invisible. */\nexport const FOCUS_RING_SELECTION_BAR =\n 'outline-none focus-visible:outline-offset-2 focus-visible:[outline:2px_solid_var(--malachite-selection-bar-foreground)]';\n\n/** Inset ring for a control on the InlineConfirm chip. */\nexport const FOCUS_RING_INLINE_CONFIRM_INSET =\n 'outline-none focus-visible:[outline:2px_solid_var(--malachite-inline-confirm-foreground)] focus-visible:[outline-offset:-2px]';\n\n/** Ring for `CloseButton surface=\"inverse\"`, where `FOCUS_RING` would be invisible on the dark panel. */\nexport const FOCUS_RING_CLOSE_BUTTON_INVERSE =\n 'outline-none focus-visible:outline-offset-2 focus-visible:[outline:2px_solid_var(--malachite-close-button-inverse-hover-foreground,rgb(255,255,255))]';\n"],"names":["FOCUS_RING","FOCUS_RING_INSET","FOCUS_RING_INSET_OVERLAY","FOCUS_RING_WITHIN_RADIO","FOCUS_RING_SELECTION_BAR","FOCUS_RING_INLINE_CONFIRM_INSET","FOCUS_RING_CLOSE_BUTTON_INVERSE"],"mappings":"AAGO,MAAMA,IACT,+GAOSC,IACT,oHAGSC,IACT,uJAUSC,IACT,qJAGSC,IACT,2HAGSC,IACT,iIAGSC,IACT;"}
package/dist/index.d.ts CHANGED
@@ -373,6 +373,11 @@ declare interface ButtonProps extends default_2.ButtonHTMLAttributes<HTMLButtonE
373
373
  disabled?: boolean;
374
374
  icon?: default_2.ReactNode;
375
375
  iconPosition?: 'start' | 'end';
376
+ /** Before the label at its own size, never squeezed (an avatar); pairs with an end `icon`. */
377
+ leading?: default_2.ReactNode;
378
+ /** `start` aligns the label to the leading edge and pins an end `icon` to the far edge. */
379
+ contentAlign?: 'center' | 'start';
380
+ /** Fills the container's width. */
376
381
  flexGrow?: boolean;
377
382
  fill?: boolean;
378
383
  tooltip?: string;
@@ -927,6 +932,28 @@ export declare interface ClampTextProps {
927
932
 
928
933
  export declare const ClearIcon: React.FC<IconProps_27>;
929
934
 
935
+ /** The bare X that dismisses a screen or panel: no box at rest or on hover. */
936
+ export declare const CloseButton: default_2.ForwardRefExoticComponent<CloseButtonProps & default_2.RefAttributes<HTMLButtonElement>>;
937
+
938
+ export declare interface CloseButtonProps extends Omit<default_2.ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'className' | 'style' | 'color' | 'type' | 'disabled' | 'onClick' | 'aria-label' | 'aria-disabled' | 'aria-busy'> {
939
+ onClick?: default_2.MouseEventHandler<HTMLButtonElement>;
940
+ /** Accessible name. Defaults to "Close"; name what closes when there are several. */
941
+ 'aria-label'?: string;
942
+ 'aria-labelledby'?: string;
943
+ disabled?: boolean;
944
+ /** Shows a spinner in place of the X, sets `aria-busy` and ignores clicks. */
945
+ loading?: boolean;
946
+ /** `small` is a 24px target, `medium` a 32px one. The glyph is 12px in both. */
947
+ size?: CloseButtonSize;
948
+ surface?: CloseButtonSurface;
949
+ className?: string;
950
+ }
951
+
952
+ export declare type CloseButtonSize = 'small' | 'medium';
953
+
954
+ /** `inverse` is for a surface that stays dark in every theme, e.g. `SplitScreenLayout`'s panel. */
955
+ export declare type CloseButtonSurface = 'default' | 'inverse';
956
+
930
957
  export declare const CloseIcon: React.FC<IconProps_28>;
931
958
 
932
959
  export declare const CodeBlock: default_2.FC<CodeBlockProps>;
@@ -3258,6 +3285,12 @@ export declare interface MalachiteTheme {
3258
3285
  divider: string;
3259
3286
  shadow: string;
3260
3287
  };
3288
+ /** `CloseButton surface="inverse"`: the X on a panel that is dark in every theme. */
3289
+ closeButton: {
3290
+ inverseForeground: string;
3291
+ inverseHoverForeground: string;
3292
+ inverseDisabledForeground: string;
3293
+ };
3261
3294
  dropdown: {
3262
3295
  border: string;
3263
3296
  borderActive: string;
@@ -3803,6 +3836,7 @@ export declare interface PartialMalachiteTheme {
3803
3836
  cardStrip?: Partial<MalachiteTheme['colors']['cardStrip']>;
3804
3837
  selectionBar?: Partial<MalachiteTheme['colors']['selectionBar']>;
3805
3838
  inlineConfirm?: Partial<MalachiteTheme['colors']['inlineConfirm']>;
3839
+ closeButton?: Partial<MalachiteTheme['colors']['closeButton']>;
3806
3840
  dropdown?: Partial<MalachiteTheme['colors']['dropdown']>;
3807
3841
  tag?: {
3808
3842
  [K in keyof MalachiteTheme['colors']['tag']]?: Partial<MalachiteTheme['colors']['tag'][K]>;
@@ -3826,6 +3860,31 @@ export declare interface PartialMalachiteTheme {
3826
3860
  };
3827
3861
  }
3828
3862
 
3863
+ export declare interface PathTextProps {
3864
+ /** Root first, leaf last; segments give way in that order, so the leaf stays whole longest. */
3865
+ segments: PathTextSegment[];
3866
+ /** Drawn between segments, never cut. Defaults to `' / '`. */
3867
+ separator?: string;
3868
+ /** Inherits the surrounding size when unset. */
3869
+ size?: FontSize;
3870
+ /** Shown when anything is cut. Defaults to the whole path; pass a node to wrap it. */
3871
+ tooltip?: default_2.ReactNode;
3872
+ tooltipPlacement?: 'top' | 'bottom' | 'left' | 'right';
3873
+ /** Holds the tooltip back, e.g. while the menu this path labels is open. */
3874
+ tooltipSuppressed?: boolean;
3875
+ className?: string;
3876
+ }
3877
+
3878
+ export declare interface PathTextSegment {
3879
+ text: string;
3880
+ tone?: PathTextTone;
3881
+ /** Kept when cut, before the next segment gives way. Defaults to 2; ignored on the last. */
3882
+ minChars?: number;
3883
+ }
3884
+
3885
+ /** `default` takes the surrounding colour and weight; `secondary` recedes, as an ancestor should. */
3886
+ export declare type PathTextTone = 'default' | 'secondary';
3887
+
3829
3888
  declare type PhraseLabel = string | {
3830
3889
  prefix: string;
3831
3890
  suffix?: string;
@@ -3913,6 +3972,8 @@ declare interface PopoverBaseProps {
3913
3972
  offset?: FloatingOffset;
3914
3973
  elevation?: 'default' | 'raised';
3915
3974
  initialFocusRef?: default_2.RefObject<HTMLElement | null>;
3975
+ /** A click panel also opens, focus inside, on the arrow key its placement points at. */
3976
+ openOnArrowKey?: boolean;
3916
3977
  role?: 'dialog' | 'menu' | 'listbox';
3917
3978
  className?: string;
3918
3979
  }
@@ -5366,6 +5427,7 @@ export declare const Typography: {
5366
5427
  LinkText: FC<LinkProps_2>;
5367
5428
  MarkdownBlock: NamedExoticComponent<MarkdownProps>;
5368
5429
  PageTitle: FC<PageTitleProps>;
5430
+ PathText: FC<PathTextProps>;
5369
5431
  SecondaryText: FC<SecondaryTextProps>;
5370
5432
  SectionTitle: FC<SectionTitleProps>;
5371
5433
  StatisticLabel: FC<StatisticLabelProps>;