@kanso-labs/kanso-ui 0.44.0 → 0.44.1

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.
@@ -4,12 +4,14 @@ import { spacing } from "../../tokens/design.tokens.stylex.js";
4
4
  import { hasContent } from "../../render/content.js";
5
5
  import { useRender } from "../../render/useRender.js";
6
6
  import { mergeStyles } from "../../styles/merge.js";
7
+ import { motionDurationMs, motionEasing } from "../../tokens/values.js";
7
8
  import Text from "../text/index.js";
8
9
  import { c } from "react/compiler-runtime";
9
- import { createElement } from "react";
10
+ import { createElement, useLayoutEffect, useRef } from "react";
10
11
  import { jsx, jsxs } from "react/jsx-runtime";
11
12
  //#region src/components/app-bar/index.tsx
12
- const GLYPH_INSET = `(48px - 24px) / 2`;
13
+ const ICON_BUTTON_TARGET = "48px";
14
+ const GLYPH_INSET = `(${ICON_BUTTON_TARGET} - 24px) / 2`;
13
15
  const HEIGHTS = {
14
16
  lg: {
15
17
  plain: "120px",
@@ -34,21 +36,22 @@ const styles = {
34
36
  },
35
37
  root: {
36
38
  k6CgDc: "x12w9bfk",
37
- kGNEyG: "x6s0dn4",
39
+ kGNEyG: "x1qjc9v5",
38
40
  kWkggS: "x14s9m32",
39
41
  kB7OPa: "x9f619",
40
42
  k1xSpc: "x78zum5",
41
43
  kjj79g: "xl56j7k",
42
44
  kIyJzY: "xioksi3",
43
- k1ekBW: "x8u3r54",
45
+ k1ekBW: "x15406qy",
44
46
  kAMwcw: "x1seskgd",
45
47
  $$css: true
46
48
  },
47
49
  row: {
48
- kGNEyG: "x6s0dn4",
50
+ kfiyM8: "x10ukxgv",
49
51
  kB7OPa: "x9f619",
50
- k1xSpc: "x78zum5",
51
- kOIVth: "xzoy561",
52
+ k1xSpc: "xrvj5dj",
53
+ kC13JO: "x97ohe",
54
+ kumcoG: "xx9t13c",
52
55
  kzqmXN: "xh8yej3",
53
56
  kUOVxO: "xvueqy4",
54
57
  $$css: true
@@ -59,13 +62,61 @@ const styles = {
59
62
  kfdmCh: "xe3zi7u",
60
63
  kt9PQ7: "x15anc7y",
61
64
  $$css: true
65
+ },
66
+ text: {
67
+ kSGwAc: "xamitd3",
68
+ kB7OPa: "x9f619",
69
+ k1xSpc: "x78zum5",
70
+ kXwgrk: "xdt5ytf",
71
+ kJuA4N: "x12gd1sl",
72
+ kE4Cay: "xkjl1po",
73
+ k7Eaqz: "xeuugli",
74
+ kHjlTd: "xj0a0fe",
75
+ k8WAf4: "x1cmllll",
76
+ $$css: true
77
+ },
78
+ textBelow: {
79
+ kSGwAc: "xnav3zx",
80
+ k8WAf4: "xt970qd",
81
+ $$css: true
82
+ },
83
+ textCenter: {
84
+ k9WMMc: "x2b8uid",
85
+ $$css: true
86
+ }
87
+ };
88
+ const twoRows = {
89
+ lg: {
90
+ kTDh7v: "x1mna9mr",
91
+ k4zj60: "x1gmr53x",
92
+ kWgvks: "xx38sju",
93
+ k5JduY: "x1s928wv",
94
+ kVb13B: "x1vlsijy",
95
+ k5jvz2: "x1fh8phi",
96
+ kC13JO: "xc3o5yk",
97
+ $$css: true
98
+ },
99
+ md: {
100
+ kTDh7v: "x1mna9mr",
101
+ k4zj60: "x1gmr53x",
102
+ kWgvks: "xx38sju",
103
+ k5JduY: "x1s928wv",
104
+ kVb13B: "x1vlsijy",
105
+ k5jvz2: "x1v6yq59",
106
+ kC13JO: "xc3o5yk",
107
+ $$css: true
62
108
  }
63
109
  };
64
110
  const HEADLINE_VARIANT = {
65
- lg: "headlineMedium",
66
- md: "headlineSmall",
111
+ lg: "displaySmall",
112
+ md: "headlineMedium",
67
113
  sm: "titleLarge"
68
114
  };
115
+ const SUBTITLE_VARIANT = {
116
+ lg: "titleMedium",
117
+ md: "labelLarge",
118
+ sm: "labelMedium"
119
+ };
69
120
  const HEADINGS = [
70
121
  "h1",
71
122
  "h2",
@@ -87,26 +138,50 @@ const _temp2 = {
87
138
  ks0D6T: "x13i5qev",
88
139
  "$$css": true
89
140
  };
90
- const rowStyles = { root: (start, end, maxInlineSize) => [
91
- _temp2,
92
- {
141
+ const rowStyles = {
142
+ root: (start, end, maxInlineSize) => [
143
+ _temp2,
144
+ {
145
+ kwRFfy: end != null ? "x120eax6" : end,
146
+ kZCmMZ: start != null ? "xnvo3vl" : start,
147
+ $$css: true
148
+ },
149
+ {
150
+ "--x-maxInlineSize": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(maxInlineSize),
151
+ "--x-paddingInlineEnd": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(end),
152
+ "--x-paddingInlineStart": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(start)
153
+ }
154
+ ],
155
+ tracks: (gridTemplateRows) => [{
156
+ k9llMU: gridTemplateRows != null ? "x1xxtoay" : gridTemplateRows,
157
+ $$css: true
158
+ }, { "--x-gridTemplateRows": gridTemplateRows != null ? gridTemplateRows : void 0 }]
159
+ };
160
+ const _temp3 = {
161
+ keoZOQ: "x1vhfslr",
162
+ "$$css": true
163
+ };
164
+ const textStyles = {
165
+ inset: (start, end) => [{
93
166
  kwRFfy: end != null ? "x120eax6" : end,
94
167
  kZCmMZ: start != null ? "xnvo3vl" : start,
95
168
  $$css: true
96
- },
97
- {
98
- "--x-maxInlineSize": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(maxInlineSize),
169
+ }, {
99
170
  "--x-paddingInlineEnd": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(end),
100
171
  "--x-paddingInlineStart": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(start)
101
- }
102
- ] };
172
+ }],
173
+ reach: (marginBlockStart) => [_temp3, { "--x-marginBlockStart": ((val) => typeof val === "number" ? val + "px" : val != null ? val : void 0)(marginBlockStart) }]
174
+ };
175
+ const UNDER_TARGETS = `calc((${COLLAPSED_HEIGHT} - ${ICON_BUTTON_TARGET}) / 2)`;
176
+ const LINES = [":scope > :is(h1, h2, h3, h4, h5, h6)", ":scope > p"];
103
177
  /**
104
178
  * Material Design's app bar: the container at the top of a page carrying its
105
179
  * title, one or two actions, and the way back out.
106
180
  *
107
181
  * Three sizes. `sm` is a fixed 64px bar for a page whose title is a label;
108
182
  * `md` and `lg` are the flexible bars, which give the headline a larger
109
- * type role and grow to fit a subtitle or a headline that wraps.
183
+ * type role in a row of its own under the slots, and grow to fit a subtitle
184
+ * or a headline that wraps.
110
185
  *
111
186
  * It paints its own surface, unlike the layout components, because separating
112
187
  * itself from the content beneath is the job — which is also why `scrolled`
@@ -114,167 +189,322 @@ const rowStyles = { root: (start, end, maxInlineSize) => [
114
189
  * element scrolls, so both are set from its own scroll handler.
115
190
  */
116
191
  function AppBar(t0) {
117
- const $ = c(39);
118
- let render;
119
- let t1;
120
- let t2;
121
- let t3;
122
- let t4;
123
- let t5;
124
- if ($[0] !== t0) {
125
- const { align: t6, collapsed: t7, contentInset: t8, contentMaxInlineSize: t9, headingLevel: t10, headline, leading, pinned: t11, render: t12, scrolled: t13, size: t14, subtitle, trailing, ...props$1 } = t0;
126
- render = t12;
127
- const align = t6 === void 0 ? "start" : t6;
128
- const collapsed = t7 === void 0 ? false : t7;
129
- const contentInset = t8 === void 0 ? spacing.lg : t8;
130
- const contentMaxInlineSize = t9 === void 0 ? "none" : t9;
131
- const headingLevel = t10 === void 0 ? 1 : t10;
132
- const pinned = t11 === void 0 ? false : t11;
133
- const scrolled = t13 === void 0 ? false : t13;
134
- const size = t14 === void 0 ? "sm" : t14;
135
- const collapse = collapsed && size !== "sm";
136
- const expandedHeight = hasContent(subtitle) ? HEIGHTS[size].withSubtitle : HEIGHTS[size].plain;
137
- const height = collapse ? COLLAPSED_HEIGHT : expandedHeight;
138
- const hasLeading = hasContent(leading);
139
- const hasTrailing = hasContent(trailing);
140
- t5 = useRender;
141
- t4 = "header";
142
- t1 = props$1;
143
- const t15 = props(styles.row, rowStyles.root(edgePadding(contentInset, hasLeading), edgePadding(contentInset, hasTrailing), contentMaxInlineSize));
144
- let t16;
145
- if ($[7] !== hasLeading || $[8] !== leading) {
146
- t16 = !hasLeading ? null : /* @__PURE__ */ jsx("div", {
147
- className: "x6s0dn4 x9f619 x78zum5 x2lah0s x1r8zjiw x1let9r4",
148
- children: leading
149
- });
150
- $[7] = hasLeading;
151
- $[8] = leading;
152
- $[9] = t16;
153
- } else t16 = $[9];
154
- let t17;
155
- if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
156
- t17 = {
157
- 0: { className: "x9f619 x78zum5 xdt5ytf x1iyjqo2 xkjl1po xl56j7k xeuugli xj0a0fe x1cmllll" },
158
- 1: { className: "x9f619 x78zum5 xdt5ytf x1iyjqo2 xkjl1po xl56j7k xeuugli xj0a0fe x1cmllll x2b8uid" }
159
- };
160
- $[10] = t17;
161
- } else t17 = $[10];
162
- const t18 = t17[!!(align === "center") << 0];
163
- let t19;
164
- if ($[11] !== collapse || $[12] !== headingLevel || $[13] !== headline || $[14] !== size) {
165
- t19 = !hasContent(headline) ? null : /* @__PURE__ */ jsx(Text, {
166
- className: "x12w9bfk xioksi3 x1nxaspg x1seskgd",
167
- render: heading(headingLevel),
168
- variant: collapse ? HEADLINE_VARIANT.sm : HEADLINE_VARIANT[size],
169
- children: headline
170
- });
171
- $[11] = collapse;
172
- $[12] = headingLevel;
173
- $[13] = headline;
174
- $[14] = size;
175
- $[15] = t19;
176
- } else t19 = $[15];
177
- let t20;
178
- if ($[16] !== collapse || $[17] !== subtitle) {
179
- t20 = !hasContent(subtitle) || collapse ? null : /* @__PURE__ */ jsx(Text, {
180
- render: PARAGRAPH,
181
- tone: "muted",
182
- variant: "bodyMedium",
183
- children: subtitle
184
- });
185
- $[16] = collapse;
186
- $[17] = subtitle;
187
- $[18] = t20;
188
- } else t20 = $[18];
189
- let t21;
190
- if ($[19] !== t18 || $[20] !== t19 || $[21] !== t20) {
191
- t21 = /* @__PURE__ */ jsxs("div", {
192
- ...t18,
193
- children: [t19, t20]
194
- });
195
- $[19] = t18;
196
- $[20] = t19;
197
- $[21] = t20;
198
- $[22] = t21;
199
- } else t21 = $[22];
200
- let t22;
201
- if ($[23] !== hasTrailing || $[24] !== trailing) {
202
- t22 = !hasTrailing ? null : /* @__PURE__ */ jsx("div", {
203
- className: "x6s0dn4 x9f619 x78zum5 x2lah0s x1r8zjiw x1let9r4",
204
- children: trailing
205
- });
206
- $[23] = hasTrailing;
207
- $[24] = trailing;
208
- $[25] = t22;
209
- } else t22 = $[25];
210
- if ($[26] !== t15 || $[27] !== t16 || $[28] !== t21 || $[29] !== t22) {
211
- t2 = /* @__PURE__ */ jsxs("div", {
212
- ...t15,
213
- children: [
214
- t16,
215
- t21,
216
- t22
217
- ]
218
- });
219
- $[26] = t15;
220
- $[27] = t16;
221
- $[28] = t21;
222
- $[29] = t22;
223
- $[30] = t2;
224
- } else t2 = $[30];
225
- t3 = mergeStyles(props(styles.root, heightStyles.root(height), scrolled && styles.scrolled, pinned && styles.pinned), props$1);
226
- $[0] = t0;
227
- $[1] = render;
228
- $[2] = t1;
229
- $[3] = t2;
230
- $[4] = t3;
231
- $[5] = t4;
232
- $[6] = t5;
233
- } else {
234
- render = $[1];
235
- t1 = $[2];
236
- t2 = $[3];
237
- t3 = $[4];
238
- t4 = $[5];
239
- t5 = $[6];
240
- }
241
- let t6;
242
- if ($[31] !== t1 || $[32] !== t2 || $[33] !== t3) {
243
- t6 = {
244
- ...t1,
245
- children: t2,
246
- ...t3
192
+ const $ = c(29);
193
+ const { align: t1, collapsed: t2, contentInset: t3, contentMaxInlineSize: t4, headingLevel: t5, headline, leading, pinned: t6, render, scrolled: t7, size: t8, subtitle, trailing, ...props$1 } = t0;
194
+ const align = t1 === void 0 ? "start" : t1;
195
+ const collapsed = t2 === void 0 ? false : t2;
196
+ const contentInset = t3 === void 0 ? spacing.lg : t3;
197
+ const contentMaxInlineSize = t4 === void 0 ? "none" : t4;
198
+ const headingLevel = t5 === void 0 ? 1 : t5;
199
+ const pinned = t6 === void 0 ? false : t6;
200
+ const scrolled = t7 === void 0 ? false : t7;
201
+ const size = t8 === void 0 ? "sm" : t8;
202
+ const collapse = collapsed && size !== "sm";
203
+ const below = size !== "sm" && !collapse;
204
+ const expandedHeight = hasContent(subtitle) ? HEIGHTS[size].withSubtitle : HEIGHTS[size].plain;
205
+ const height = collapse ? COLLAPSED_HEIGHT : expandedHeight;
206
+ const hasLeading = hasContent(leading);
207
+ const hasTrailing = hasContent(trailing);
208
+ const textRef = useCollapseMotion(collapse, align);
209
+ const t9 = "header";
210
+ const t10 = props(styles.row, rowStyles.root(edgePadding(contentInset, hasLeading), edgePadding(contentInset, hasTrailing), contentMaxInlineSize), rowStyles.tracks(below ? `minmax(${COLLAPSED_HEIGHT}, auto) 1fr` : `minmax(${COLLAPSED_HEIGHT}, auto)`), heightStyles.root(height), below && twoRows[size]);
211
+ const t11 = !hasLeading ? null : /* @__PURE__ */ jsx("div", {
212
+ className: "x6s0dn4 xamitd3 x9f619 x78zum5 x1r8zjiw x1let9r4 x1kxrqrs x15ir4sr",
213
+ children: leading
214
+ });
215
+ const t12 = props(styles.text, below && styles.textBelow, below && textStyles.inset(glyphRoom(contentInset, hasLeading), glyphRoom(contentInset, hasTrailing)), below && textStyles.reach(`calc(-1 * ${UNDER_TARGETS})`), align === "center" && styles.textCenter);
216
+ let t13;
217
+ if ($[0] !== collapse || $[1] !== headingLevel || $[2] !== headline || $[3] !== size) {
218
+ t13 = !hasContent(headline) ? null : /* @__PURE__ */ jsx(Text, {
219
+ render: heading(headingLevel),
220
+ variant: collapse ? HEADLINE_VARIANT.sm : HEADLINE_VARIANT[size],
221
+ children: headline
222
+ });
223
+ $[0] = collapse;
224
+ $[1] = headingLevel;
225
+ $[2] = headline;
226
+ $[3] = size;
227
+ $[4] = t13;
228
+ } else t13 = $[4];
229
+ let t14;
230
+ if ($[5] !== collapse || $[6] !== size || $[7] !== subtitle) {
231
+ t14 = !hasContent(subtitle) ? null : /* @__PURE__ */ jsx(Text, {
232
+ render: PARAGRAPH,
233
+ tone: "muted",
234
+ variant: collapse ? SUBTITLE_VARIANT.sm : SUBTITLE_VARIANT[size],
235
+ children: subtitle
236
+ });
237
+ $[5] = collapse;
238
+ $[6] = size;
239
+ $[7] = subtitle;
240
+ $[8] = t14;
241
+ } else t14 = $[8];
242
+ let t15;
243
+ if ($[9] !== t12 || $[10] !== t13 || $[11] !== t14 || $[12] !== textRef) {
244
+ t15 = /* @__PURE__ */ jsxs("div", {
245
+ ...t12,
246
+ ref: textRef,
247
+ children: [t13, t14]
248
+ });
249
+ $[9] = t12;
250
+ $[10] = t13;
251
+ $[11] = t14;
252
+ $[12] = textRef;
253
+ $[13] = t15;
254
+ } else t15 = $[13];
255
+ let t16;
256
+ if ($[14] !== hasTrailing || $[15] !== trailing) {
257
+ t16 = !hasTrailing ? null : /* @__PURE__ */ jsx("div", {
258
+ className: "x6s0dn4 xamitd3 x9f619 x78zum5 x1r8zjiw x1let9r4 x1txd71x x6aqprb",
259
+ children: trailing
260
+ });
261
+ $[14] = hasTrailing;
262
+ $[15] = trailing;
263
+ $[16] = t16;
264
+ } else t16 = $[16];
265
+ let t17;
266
+ if ($[17] !== t10 || $[18] !== t11 || $[19] !== t15 || $[20] !== t16) {
267
+ t17 = /* @__PURE__ */ jsxs("div", {
268
+ ...t10,
269
+ children: [
270
+ t11,
271
+ t15,
272
+ t16
273
+ ]
274
+ });
275
+ $[17] = t10;
276
+ $[18] = t11;
277
+ $[19] = t15;
278
+ $[20] = t16;
279
+ $[21] = t17;
280
+ } else t17 = $[21];
281
+ const t18 = mergeStyles(props(styles.root, heightStyles.root(height), scrolled && styles.scrolled, pinned && styles.pinned), props$1);
282
+ let t19;
283
+ if ($[22] !== props$1 || $[23] !== t17 || $[24] !== t18) {
284
+ t19 = {
285
+ ...props$1,
286
+ children: t17,
287
+ ...t18
247
288
  };
248
- $[31] = t1;
249
- $[32] = t2;
250
- $[33] = t3;
251
- $[34] = t6;
252
- } else t6 = $[34];
253
- let t7;
254
- if ($[35] !== render || $[36] !== t4 || $[37] !== t6) {
255
- t7 = {
256
- defaultTagName: t4,
257
- props: t6,
289
+ $[22] = props$1;
290
+ $[23] = t17;
291
+ $[24] = t18;
292
+ $[25] = t19;
293
+ } else t19 = $[25];
294
+ let t20;
295
+ if ($[26] !== render || $[27] !== t19) {
296
+ t20 = {
297
+ defaultTagName: t9,
298
+ props: t19,
258
299
  render
259
300
  };
260
- $[35] = render;
261
- $[36] = t4;
262
- $[37] = t6;
263
- $[38] = t7;
264
- } else t7 = $[38];
265
- return t5(t7);
301
+ $[26] = render;
302
+ $[27] = t19;
303
+ $[28] = t20;
304
+ } else t20 = $[28];
305
+ return useRender(t20);
266
306
  }
267
307
  /**
268
- * The row's padding on one edge, for the inset the call site gave.
269
- *
270
- * A bare `0` is the one length CSS accepts without a unit, and the one it
271
- * rejects inside `max()`, where it reads as a number, so it is given its
272
- * unit before the arithmetic sees it.
308
+ * Where an animation still under way has the bar and the text drawn: the
309
+ * height it holds the bar at, and each line where its transform has taken it
310
+ * and at the size its scale has reached. A line scales about the point it
311
+ * hangs from, so the transform's translation is how far that point has moved.
273
312
  */
313
+ function drawnAt(bar, text, settled) {
314
+ return {
315
+ height: bar.getBoundingClientRect().height,
316
+ lines: LINES.map((selector, index) => {
317
+ const line = settled.lines[index];
318
+ const element = text.querySelector(selector);
319
+ if (line === null || element === null) return null;
320
+ const moved = matrixOf(element);
321
+ return {
322
+ size: line.size * moved.a,
323
+ x: line.x + moved.e,
324
+ y: line.y + moved.f
325
+ };
326
+ })
327
+ };
328
+ }
329
+ /** The row's padding on one edge, for the inset the call site gave. */
274
330
  function edgePadding(contentInset, hasSlot) {
275
- const inset = contentInset.trim() === "0" ? "0px" : contentInset;
331
+ const inset = withUnit(contentInset);
276
332
  return hasSlot ? `max(0px, ${inset} - ${GLYPH_INSET})` : inset;
277
333
  }
334
+ /**
335
+ * Where the bar and each of the text's lines are drawn. A line's point is on
336
+ * the edge its text hangs from: the middle when it is centred, and the start
337
+ * edge otherwise, which is the right edge in a right-to-left page. Each line
338
+ * is as wide as the text block, so that edge is its box's.
339
+ */
340
+ function geometryOf(bar, text, align) {
341
+ const frame = bar.getBoundingClientRect();
342
+ return {
343
+ height: frame.height,
344
+ lines: LINES.map((selector) => {
345
+ const element = text.querySelector(selector);
346
+ if (element === null) return null;
347
+ const box = element.getBoundingClientRect();
348
+ const edge = align === "center" ? (box.left + box.right) / 2 : isRightToLeft(element) ? box.right : box.left;
349
+ return {
350
+ size: Number.parseFloat(getComputedStyle(element).fontSize),
351
+ x: edge - frame.left,
352
+ y: glyphTopOf(element) - frame.top
353
+ };
354
+ })
355
+ };
356
+ }
357
+ /**
358
+ * What the row's padding held back on one edge for a slot's glyph, which the
359
+ * text in the second row puts back: the inset less `edgePadding`, so the
360
+ * smaller of the inset and GLYPH_INSET.
361
+ */
362
+ function glyphRoom(contentInset, hasSlot) {
363
+ return hasSlot ? `min(${withUnit(contentInset)}, ${GLYPH_INSET})` : "0px";
364
+ }
365
+ /**
366
+ * Where an element's first glyphs start, which is below its box's top by
367
+ * half its line's leading. The leading is a share of the line height, and
368
+ * the two roles a line moves between do not keep line height and type size
369
+ * in one ratio, so a line scaled by its type size from its box's corner
370
+ * starts its glyphs off where they were drawn: about a pixel, for a large
371
+ * bar's headline.
372
+ */
373
+ function glyphTopOf(element) {
374
+ const range = document.createRange();
375
+ range.selectNodeContents(element);
376
+ const first = range.getClientRects().item(0);
377
+ return first === null ? element.getBoundingClientRect().top : first.top;
378
+ }
379
+ /** Whether the text runs right to left, which decides the edge it hangs from. */
380
+ function isRightToLeft(element) {
381
+ return getComputedStyle(element).direction === "rtl";
382
+ }
383
+ /** The transform an element is drawn with, `none` reading as no change. */
384
+ function matrixOf(element) {
385
+ return new DOMMatrixReadOnly(getComputedStyle(element).transform);
386
+ }
387
+ /**
388
+ * The point a line scales about, in its own box: the top of its glyphs, on
389
+ * the edge its text hangs from.
390
+ */
391
+ function originOf(element, align) {
392
+ return `${align === "center" ? "center" : isRightToLeft(element) ? "right" : "left"} ${glyphTopOf(element) - element.getBoundingClientRect().top}px`;
393
+ }
394
+ /**
395
+ * Read when the bar changes state rather than once at mount, so a reader who
396
+ * changes the setting is answered by the next collapse.
397
+ */
398
+ function prefersReducedMotion() {
399
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
400
+ }
401
+ /**
402
+ * Animates a flexible bar between its two states, and returns the ref for the
403
+ * text block whose lines it moves.
404
+ *
405
+ * Collapsing changes the bar's layout rather than a value — the text leaves
406
+ * the second row for the strip, both lines take the small bar's type roles,
407
+ * and the bar's height drops from a minimum to whatever its content takes —
408
+ * so there is nothing for a CSS transition to interpolate. This plays it the
409
+ * other way round: the change has already been laid out when the effect runs,
410
+ * so the bar is animated from the height it had to the one it has, and each
411
+ * line from where and how large it was drawn to where and how large it is,
412
+ * by a transform that starts at the difference and ends at none. A line
413
+ * scales about the point its glyphs hang from, which is what lands its first
414
+ * frame on the glyphs it replaces — see glyphTopOf. A scale rather than a
415
+ * transition of the type itself, which would lay the text out again on every
416
+ * frame and move it away from the place it was measured at. Each line moves
417
+ * on its own rather than with the block, since the subtitle starts under the
418
+ * headline's line, whose height changes with its role. The slots need
419
+ * nothing: they are in the strip in both states.
420
+ *
421
+ * The bar's height is animated through `min-block-size` as well as its
422
+ * height, since the minimum would otherwise hold an expanding bar at its new
423
+ * height from the first frame, and it is clipped while it moves, since an
424
+ * expanding bar's second row is laid out at full height from the start.
425
+ *
426
+ * Where the text was is measured after every commit that leaves the state
427
+ * alone, so it is current when the state does change. A change that lands
428
+ * while the last one is still moving starts from where the bar and the text
429
+ * are drawn at that moment, read off the running animations, so reversing
430
+ * mid-way turns round rather than jumping to the end first.
431
+ *
432
+ * Its timing is the root's `background-color` transition's, the `medium1`
433
+ * duration and the emphasized curve, so the fill and the height move as one.
434
+ * Those come from tokens/values rather than the theme, since `animate()`
435
+ * throws on a var(), and the motion is skipped under reduced motion, as the
436
+ * root's transition is.
437
+ */
438
+ function useCollapseMotion(collapse, align) {
439
+ const $ = c(4);
440
+ const textRef = useRef(null);
441
+ const settled = useRef(null);
442
+ const collapsedBefore = useRef(collapse);
443
+ let t0;
444
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
445
+ t0 = [];
446
+ $[0] = t0;
447
+ } else t0 = $[0];
448
+ const animations = useRef(t0);
449
+ let t1;
450
+ if ($[1] !== align || $[2] !== collapse) {
451
+ t1 = () => {
452
+ const text = textRef.current;
453
+ const bar = text?.parentElement?.parentElement;
454
+ if (text == null || bar == null) return;
455
+ const moving = animations.current.some(_temp4);
456
+ if (collapsedBefore.current === collapse) {
457
+ if (!moving) settled.current = geometryOf(bar, text, align);
458
+ return;
459
+ }
460
+ collapsedBefore.current = collapse;
461
+ const last = settled.current;
462
+ const from = moving && last !== null ? drawnAt(bar, text, last) : last;
463
+ for (const animation_0 of animations.current) animation_0.cancel();
464
+ animations.current = [];
465
+ const to = geometryOf(bar, text, align);
466
+ settled.current = to;
467
+ if (from === null || prefersReducedMotion()) return;
468
+ const timing = {
469
+ duration: motionDurationMs.medium1,
470
+ easing: motionEasing.emphasized
471
+ };
472
+ const heights = [`${from.height}px`, `${to.height}px`];
473
+ animations.current = [bar.animate({
474
+ blockSize: heights,
475
+ clipPath: ["inset(0)", "inset(0)"],
476
+ minBlockSize: heights
477
+ }, timing)];
478
+ for (const [index, selector] of LINES.entries()) {
479
+ const element = text.querySelector(selector);
480
+ const was = from.lines[index];
481
+ const is = to.lines[index];
482
+ if (element === null || was == null || is == null || is.size === 0) continue;
483
+ const origin = originOf(element, align);
484
+ animations.current.push(element.animate({
485
+ transform: [`translate(${was.x - is.x}px, ${was.y - is.y}px) scale(${was.size / is.size})`, "none"],
486
+ transformOrigin: [origin, origin]
487
+ }, timing));
488
+ }
489
+ };
490
+ $[1] = align;
491
+ $[2] = collapse;
492
+ $[3] = t1;
493
+ } else t1 = $[3];
494
+ useLayoutEffect(t1);
495
+ return textRef;
496
+ }
497
+ /**
498
+ * The inset as a length CSS arithmetic accepts. A bare `0` is the one length
499
+ * CSS takes without a unit, and the one it rejects inside `max()` and `min()`,
500
+ * where it reads as a number.
501
+ */
502
+ function _temp4(animation) {
503
+ return animation.playState === "running";
504
+ }
505
+ function withUnit(contentInset) {
506
+ return contentInset.trim() === "0" ? "0px" : contentInset;
507
+ }
278
508
  //#endregion
279
509
  export { AppBar as default };
280
510