vireuikit 0.8.0 → 0.9.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.
package/dist/index.cjs CHANGED
@@ -120,15 +120,15 @@ function postureFromSegments(window, segments) {
120
120
  return POSTURE_FLAT;
121
121
  }
122
122
  var area = (r) => r ? r.width * r.height : 0;
123
- function segmentOf(box, segments) {
124
- const cx = box.x + box.width / 2;
125
- const cy = box.y + box.height / 2;
123
+ function segmentOf(box2, segments) {
124
+ const cx = box2.x + box2.width / 2;
125
+ const cy = box2.y + box2.height / 2;
126
126
  const holding = segments.find((s) => cx >= s.x && cx < s.x + s.width && cy >= s.y && cy < s.y + s.height);
127
127
  if (holding) return holding;
128
128
  let best;
129
129
  let most = 0;
130
130
  for (const s of segments) {
131
- const overlap = area(intersectRect(s, box));
131
+ const overlap = area(intersectRect(s, box2));
132
132
  if (overlap > most) {
133
133
  most = overlap;
134
134
  best = s;
@@ -203,7 +203,7 @@ function growthFrame(source, target, anchor, phase) {
203
203
  const len = Math.hypot(dx, dy) || 1;
204
204
  const sourceSize = vireglass.halfMinDp(source.geometry) * 2;
205
205
  const lift = DROP_LIFT * sourceSize;
206
- const drop = {
206
+ const drop2 = {
207
207
  x: source.center.x + dx / len * lift,
208
208
  y: source.center.y + dy / len * lift
209
209
  };
@@ -222,13 +222,13 @@ function growthFrame(source, target, anchor, phase) {
222
222
  );
223
223
  const shrunk = {
224
224
  geometry: shrunkGeometry,
225
- center: { x: mix(source.center.x, drop.x, a), y: mix(source.center.y, drop.y, a) }
225
+ center: { x: mix(source.center.x, drop2.x, a), y: mix(source.center.y, drop2.y, a) }
226
226
  };
227
227
  const seed = d * Math.sqrt(a);
228
228
  const cornerX = Math.sign(anchor.x - source.center.x);
229
229
  const cornerY = Math.sign(anchor.y - source.center.y);
230
- const edgeX = mix(drop.x + cornerX * (seed / 2), anchor.x, Math.min(b, 1));
231
- const edgeY = mix(drop.y + cornerY * (seed / 2), anchor.y, Math.min(b, 1));
230
+ const edgeX = mix(drop2.x + cornerX * (seed / 2), anchor.x, Math.min(b, 1));
231
+ const edgeY = mix(drop2.y + cornerY * (seed / 2), anchor.y, Math.min(b, 1));
232
232
  const width = mix(seed, target.geometry.width, b);
233
233
  const height = mix(seed, target.geometry.height, b);
234
234
  const grownGeometry = { width, height, cornerRadius: 0 };
@@ -324,14 +324,14 @@ var OUT_END = 0.45;
324
324
  var IN_START = 0.55;
325
325
  function formMorphFrame(from, to, state) {
326
326
  const t = Math.min(Math.max(state.t.x, 0), 1);
327
- const mix2 = (a, b) => a + (b - a) * t;
327
+ const mix3 = (a, b) => a + (b - a) * t;
328
328
  return {
329
329
  box: {
330
- x: mix2(from.x, to.x),
331
- y: mix2(from.y, to.y),
332
- width: mix2(from.width, to.width),
333
- height: mix2(from.height, to.height),
334
- radius: mix2(from.radius, to.radius)
330
+ x: mix3(from.x, to.x),
331
+ y: mix3(from.y, to.y),
332
+ width: mix3(from.width, to.width),
333
+ height: mix3(from.height, to.height),
334
+ radius: mix3(from.radius, to.radius)
335
335
  },
336
336
  ink: inkAt(state, t),
337
337
  showing: t < 0.5 ? "from" : "to"
@@ -387,8 +387,8 @@ function menuSize(size) {
387
387
  labelPad: spec.paddingX
388
388
  };
389
389
  }
390
- function nestedRadius(outerRadius, inset, minimum = 0) {
391
- return vireglass.concentricRadius(outerRadius, inset, minimum);
390
+ function nestedRadius(outerRadius, inset2, minimum = 0) {
391
+ return vireglass.concentricRadius(outerRadius, inset2, minimum);
392
392
  }
393
393
  function defaultSize(input) {
394
394
  return input.touch ? "touch" : "medium";
@@ -628,10 +628,10 @@ var PRESSED_BODY = { alpha: 0.34, white: 0.35, lift: 0 };
628
628
  var FROST_LIFT = 0.26;
629
629
  function atomBody(base, state = ATOM_AT_REST, frost = false) {
630
630
  const on = activation(state);
631
- const mix2 = (key) => REST_BODY[key] + (PRESSED_BODY[key] - REST_BODY[key]) * on;
632
- let alpha = mix2("alpha");
633
- let white = mix2("white");
634
- let lift = mix2("lift");
631
+ const mix3 = (key) => REST_BODY[key] + (PRESSED_BODY[key] - REST_BODY[key]) * on;
632
+ let alpha = mix3("alpha");
633
+ let white = mix3("white");
634
+ let lift = mix3("lift");
635
635
  if (state.disabled) alpha *= DISABLED_PRESENCE;
636
636
  if (frost) {
637
637
  const density = vireglass.applyAccessibility(vireglass.resolveOptics(base), {
@@ -876,7 +876,7 @@ function menuSurfaceFrame(trigger, menu, anchor, phase, bounds) {
876
876
  phase
877
877
  );
878
878
  const { geometry, center: c } = frame.body;
879
- let box = {
879
+ let box2 = {
880
880
  x: c.x - geometry.width / 2,
881
881
  y: c.y - geometry.height / 2,
882
882
  width: geometry.width,
@@ -884,17 +884,17 @@ function menuSurfaceFrame(trigger, menu, anchor, phase, bounds) {
884
884
  radius: geometry.cornerRadius
885
885
  };
886
886
  let morph = frame.merge;
887
- const held = bounds ? intersectRect(box, bounds) : null;
888
- if (held && (held.width < box.width || held.height < box.height)) {
889
- const was = center(box);
887
+ const held = bounds ? intersectRect(box2, bounds) : null;
888
+ if (held && (held.width < box2.width || held.height < box2.height)) {
889
+ const was = center(box2);
890
890
  const now = center(held);
891
- box = { ...held, radius: Math.min(box.radius, held.width / 2, held.height / 2) };
891
+ box2 = { ...held, radius: Math.min(box2.radius, held.width / 2, held.height / 2) };
892
892
  if (morph) morph = { ...morph, offsetX: morph.offsetX + was.x - now.x, offsetY: morph.offsetY + was.y - now.y };
893
893
  }
894
894
  return {
895
- box,
895
+ box: box2,
896
896
  morph,
897
- content: { x: menu.x - box.x, y: menu.y - box.y },
897
+ content: { x: menu.x - box2.x, y: menu.y - box2.y },
898
898
  ink: frame.ink,
899
899
  activity: menuActivity(phase)
900
900
  };
@@ -910,8 +910,8 @@ function menuBody(base, activity, frost = false) {
910
910
  const rest = atomBody(base, ATOM_AT_REST, frost);
911
911
  if (frost) return rest;
912
912
  const k = Math.min(Math.max(activity, 0), 1);
913
- const mix2 = (key) => rest[key] + (MENU_DROP_BODY[key] - rest[key]) * k;
914
- return { alpha: mix2("alpha"), white: mix2("white"), lift: mix2("lift") };
913
+ const mix3 = (key) => rest[key] + (MENU_DROP_BODY[key] - rest[key]) * k;
914
+ return { alpha: mix3("alpha"), white: mix3("white"), lift: mix3("lift") };
915
915
  }
916
916
  var MENU_ITEM_FILL = { active: 0.1, pressed: 0.18 };
917
917
  function menuItemFill(state) {
@@ -920,6 +920,532 @@ function menuItemFill(state) {
920
920
  return state.active ? MENU_ITEM_FILL.active : 0;
921
921
  }
922
922
 
923
+ // src/kit/nav/layout.ts
924
+ var NAV_SIZES = {
925
+ bar: { height: 56, item: 74, minItem: 44, inset: 3, margin: 16, searchGap: 8, minimized: 44 },
926
+ rail: { width: 80, item: 56, gap: 4, padding: 8, margin: 8, head: 48 },
927
+ sidebar: { width: 280, row: { touch: 44, pointer: 36 }, padding: 8, margin: 8, head: 48, separator: 12 },
928
+ accessory: { height: 48, gap: 8, inline: 44, content: 56, margin: 8 },
929
+ lens: { inflate: 6 },
930
+ toggle: 44
931
+ };
932
+ var box = (x, y, width, height, radius) => ({ x, y, width, height, radius });
933
+ function barLayout(input, items, minimized) {
934
+ const { bar, accessory } = NAV_SIZES;
935
+ const { width, height } = input.window;
936
+ const bottom = Math.max(input.bottomInset ?? 0, bar.margin);
937
+ const hasSearch = input.search?.form === "button";
938
+ const placement = input.accessory ? bottomAccessory({ form: "bar" }, { barMinimized: minimized }).placement : null;
939
+ const selected = input.selected >= 0 && input.selected < items ? input.selected : -1;
940
+ if (minimized) {
941
+ const centre = height - bottom - bar.height / 2;
942
+ const d = bar.minimized;
943
+ const body2 = box(bar.margin, centre - d / 2, d, d, d / 2);
944
+ const search2 = hasSearch ? box(width - bar.margin - d, body2.y, d, d, d / 2) : null;
945
+ let acc2 = null;
946
+ if (placement) {
947
+ const x2 = body2.x + d + accessory.gap;
948
+ const right = search2 ? search2.x - accessory.gap : width - bar.margin;
949
+ acc2 = box(x2, body2.y, Math.max(0, right - x2), accessory.inline, accessory.inline / 2);
950
+ }
951
+ return {
952
+ form: "bar",
953
+ minimized: true,
954
+ body: body2,
955
+ items: Array.from({ length: items }, () => ({ ...body2 })),
956
+ pill: selected >= 0 ? { ...body2 } : null,
957
+ search: search2,
958
+ accessory: acc2,
959
+ toggle: null,
960
+ accessoryPlacement: placement,
961
+ insets: { bottom: height - body2.y, start: 0 }
962
+ };
963
+ }
964
+ const searchBlock = hasSearch ? bar.height + bar.searchGap : 0;
965
+ const fit = items > 0 ? (width - 2 * bar.margin - searchBlock - 2 * bar.inset) / items : bar.item;
966
+ const itemWidth = Math.max(bar.minItem, Math.min(bar.item, fit));
967
+ const bodyWidth = items * itemWidth + 2 * bar.inset;
968
+ const x = Math.max(bar.margin, (width - bodyWidth - searchBlock) / 2);
969
+ const body = box(x, height - bottom - bar.height, bodyWidth, bar.height, bar.height / 2);
970
+ const cellHeight = bar.height - 2 * bar.inset;
971
+ const cells = Array.from(
972
+ { length: items },
973
+ (_, i) => box(x + bar.inset + i * itemWidth, body.y + bar.inset, itemWidth, cellHeight, cellHeight / 2)
974
+ );
975
+ const search = hasSearch ? box(x + bodyWidth + bar.searchGap, body.y, bar.height, bar.height, bar.height / 2) : null;
976
+ let acc = null;
977
+ if (placement) {
978
+ acc = box(body.x, body.y - accessory.gap - accessory.height, bodyWidth + searchBlock, accessory.height, accessory.height / 2);
979
+ }
980
+ return {
981
+ form: "bar",
982
+ minimized: false,
983
+ body,
984
+ items: cells,
985
+ pill: selected >= 0 ? { ...cells[selected] } : null,
986
+ search,
987
+ accessory: acc,
988
+ toggle: null,
989
+ accessoryPlacement: placement,
990
+ insets: { bottom: height - body.y + (acc ? accessory.height + accessory.gap : 0), start: 0 }
991
+ };
992
+ }
993
+ function contentAccessory(input, start) {
994
+ const { accessory } = NAV_SIZES;
995
+ const base = input.bottomInset ?? 0;
996
+ if (!input.accessory) return { box: null, bottom: base };
997
+ const bottom = Math.max(base, accessory.margin);
998
+ const width = Math.max(0, input.window.width - start - accessory.margin);
999
+ const acc = box(start, input.window.height - bottom - accessory.content, width, accessory.content, accessory.content / 2);
1000
+ return { box: acc, bottom: bottom + accessory.content };
1001
+ }
1002
+ function headToggle(body, padding, head, dx) {
1003
+ const d = NAV_SIZES.toggle;
1004
+ return box(body.x + dx, body.y + padding + (head - d) / 2, d, d, d / 2);
1005
+ }
1006
+ function inset(b, d) {
1007
+ return box(b.x + d, b.y + d, b.width - 2 * d, b.height - 2 * d, (Math.min(b.width, b.height) - 2 * d) / 2);
1008
+ }
1009
+ function railLayout(input, items) {
1010
+ const { rail, bar } = NAV_SIZES;
1011
+ const hasSearch = input.search?.form === "item";
1012
+ const n = items + (hasSearch ? 1 : 0);
1013
+ const head = input.toggle ? rail.head : 0;
1014
+ const natural = rail.padding * 2 + head + n * rail.item + Math.max(n - 1, 0) * rail.gap;
1015
+ const height = Math.min(natural, Math.max(input.window.height - 2 * rail.margin, 0));
1016
+ const body = box(rail.margin, rail.margin, rail.width, height, 28);
1017
+ const cell = (i) => box(body.x + rail.padding, body.y + rail.padding + head + i * (rail.item + rail.gap), rail.width - 2 * rail.padding, rail.item, 24);
1018
+ const cells = Array.from({ length: items }, (_, i) => cell(i));
1019
+ const selected = input.selected >= 0 && input.selected < items ? input.selected : -1;
1020
+ const start = body.x + body.width + rail.margin;
1021
+ const acc = contentAccessory(input, start);
1022
+ return {
1023
+ form: "rail",
1024
+ minimized: false,
1025
+ body,
1026
+ items: cells,
1027
+ pill: selected >= 0 ? inset(cells[selected], bar.inset) : null,
1028
+ search: hasSearch ? cell(items) : null,
1029
+ accessory: acc.box,
1030
+ toggle: input.toggle ? headToggle(body, rail.padding, rail.head, (rail.width - NAV_SIZES.toggle) / 2) : null,
1031
+ accessoryPlacement: acc.box ? "content-bottom" : null,
1032
+ insets: { bottom: acc.bottom, start }
1033
+ };
1034
+ }
1035
+ function sidebarLayout(input, items, secondary) {
1036
+ const { sidebar } = NAV_SIZES;
1037
+ const row = sidebar.row[input.tier];
1038
+ const body = box(sidebar.margin, sidebar.margin, sidebar.width, input.window.height - 2 * sidebar.margin, 24);
1039
+ const rowWidth = sidebar.width - 2 * sidebar.padding;
1040
+ const rowAt = (y2) => box(body.x + sidebar.padding, y2, rowWidth, row, row / 2);
1041
+ let y = body.y + sidebar.padding + (input.toggle ? sidebar.head : 0);
1042
+ let search = null;
1043
+ if (input.search?.form === "field") {
1044
+ search = rowAt(y);
1045
+ y += row;
1046
+ }
1047
+ const cells = [];
1048
+ for (let i = 0; i < items; i += 1, y += row) cells.push(rowAt(y));
1049
+ if (secondary > 0) y += sidebar.separator;
1050
+ for (let i = 0; i < secondary; i += 1, y += row) cells.push(rowAt(y));
1051
+ const selected = input.selected >= 0 && input.selected < cells.length ? input.selected : -1;
1052
+ const start = body.x + body.width + sidebar.margin;
1053
+ const acc = contentAccessory(input, start);
1054
+ return {
1055
+ form: "sidebar",
1056
+ minimized: false,
1057
+ body,
1058
+ items: cells,
1059
+ pill: selected >= 0 ? { ...cells[selected] } : null,
1060
+ search,
1061
+ accessory: acc.box,
1062
+ toggle: input.toggle ? headToggle(body, sidebar.padding, sidebar.head, sidebar.width - sidebar.padding - NAV_SIZES.toggle) : null,
1063
+ accessoryPlacement: acc.box ? "content-bottom" : null,
1064
+ insets: { bottom: acc.bottom, start }
1065
+ };
1066
+ }
1067
+ function navLayout(input) {
1068
+ const items = Math.max(0, Math.floor(input.items));
1069
+ const secondary = Math.max(0, Math.floor(input.secondary));
1070
+ if (input.form === "bar") return barLayout(input, items, !!input.minimized);
1071
+ return input.form === "rail" ? railLayout(input, items) : sidebarLayout(input, items, secondary);
1072
+ }
1073
+
1074
+ // src/kit/nav/drag.ts
1075
+ var MAX_STRETCH = 0.25;
1076
+ var SATURATION_SPEED = 1500;
1077
+ var STRETCH_TAU = 0.05;
1078
+ var OVERHANG = 4;
1079
+ var LAND = { response: 0.2, damping: 0.8 };
1080
+ var LIFT = { response: 0.2, damping: 1 };
1081
+ var createNavDrag = () => ({
1082
+ active: false,
1083
+ x: { x: 0, v: 0, target: 0 },
1084
+ lift: { x: 0, v: 0, target: 0 },
1085
+ stretch: 0,
1086
+ index: 0,
1087
+ lastX: 0
1088
+ });
1089
+ var draggable = (layout) => layout.form === "bar" && !layout.minimized && layout.items.length > 0;
1090
+ function lensSize(layout) {
1091
+ const cell = layout.items[0];
1092
+ const pad = 2 * NAV_SIZES.lens.inflate;
1093
+ return { width: cell.width + pad, height: cell.height + pad };
1094
+ }
1095
+ function clampCentre(layout, x) {
1096
+ const half = lensSize(layout).width / 2;
1097
+ const { body } = layout;
1098
+ return Math.min(Math.max(x, body.x + half - OVERHANG), body.x + body.width - half + OVERHANG);
1099
+ }
1100
+ function indexAt(layout, x) {
1101
+ const first2 = layout.items[0];
1102
+ const i = Math.floor((x - first2.x) / first2.width);
1103
+ return Math.min(Math.max(i, 0), layout.items.length - 1);
1104
+ }
1105
+ var centreOf = (layout, index) => layout.items[index].x + layout.items[index].width / 2;
1106
+ function drop(state) {
1107
+ state.active = false;
1108
+ state.lift.x = 0;
1109
+ state.lift.v = 0;
1110
+ state.lift.target = 0;
1111
+ }
1112
+ function settleTowards(state, layout, index) {
1113
+ state.active = false;
1114
+ state.index = index;
1115
+ state.x.target = centreOf(layout, index);
1116
+ state.lift.target = 0;
1117
+ }
1118
+ function pressNavDrag(state, layout, pointerX) {
1119
+ if (!draggable(layout)) return;
1120
+ const x = clampCentre(layout, pointerX);
1121
+ state.active = true;
1122
+ state.x.x = x;
1123
+ state.x.v = 0;
1124
+ state.x.target = x;
1125
+ state.lift.target = 1;
1126
+ state.stretch = 0;
1127
+ state.lastX = pointerX;
1128
+ state.index = indexAt(layout, x);
1129
+ }
1130
+ function moveNavDrag(state, layout, pointerX, dt) {
1131
+ if (!state.active || !draggable(layout)) return;
1132
+ const x = clampCentre(layout, pointerX);
1133
+ state.x.x = x;
1134
+ state.x.v = 0;
1135
+ state.x.target = x;
1136
+ const speed = dt > 0 ? Math.abs(pointerX - state.lastX) / dt : 0;
1137
+ const goal = MAX_STRETCH * Math.min(speed / SATURATION_SPEED, 1);
1138
+ state.stretch += (goal - state.stretch) * (dt > 0 ? 1 - Math.exp(-dt / STRETCH_TAU) : 1);
1139
+ state.lastX = pointerX;
1140
+ state.index = indexAt(layout, x);
1141
+ }
1142
+ function releaseNavDrag(state, layout) {
1143
+ if (!draggable(layout)) {
1144
+ drop(state);
1145
+ return state.index;
1146
+ }
1147
+ const index = indexAt(layout, state.x.x);
1148
+ settleTowards(state, layout, index);
1149
+ return index;
1150
+ }
1151
+ function cancelNavDrag(state, layout, selected) {
1152
+ if (!draggable(layout)) {
1153
+ drop(state);
1154
+ return;
1155
+ }
1156
+ const index = selected >= 0 && selected < layout.items.length ? selected : indexAt(layout, state.x.x);
1157
+ settleTowards(state, layout, index);
1158
+ }
1159
+ function stepNavDrag(state, dt, reduceMotion = false) {
1160
+ let moving = state.active;
1161
+ if (state.stretch > 0) {
1162
+ state.stretch *= Math.exp(-Math.max(dt, 0) / STRETCH_TAU);
1163
+ if (reduceMotion || state.stretch < 1e-3) state.stretch = 0;
1164
+ else moving = true;
1165
+ }
1166
+ if (!state.active && stepSpring(state.x, dt, LAND.response, LAND.damping, reduceMotion)) moving = true;
1167
+ if (stepSpring(state.lift, dt, LIFT.response, LIFT.damping, reduceMotion)) moving = true;
1168
+ return moving;
1169
+ }
1170
+ function navDragFrame(state, layout) {
1171
+ if (!draggable(layout)) return null;
1172
+ const lift = Math.min(Math.max(state.lift.x, 0), 1);
1173
+ if (!state.active && lift === 0 && state.x.x === state.x.target) return null;
1174
+ const size = lensSize(layout);
1175
+ const width = size.width * (1 + state.stretch);
1176
+ const height = size.height * (1 - state.stretch / 2);
1177
+ const cy = layout.body.y + layout.body.height / 2;
1178
+ return {
1179
+ lens: { x: state.x.x - width / 2, y: cy - height / 2, width, height, radius: height / 2 },
1180
+ lift,
1181
+ hover: indexAt(layout, state.x.x)
1182
+ };
1183
+ }
1184
+
1185
+ // src/kit/nav/form.ts
1186
+ var SEARCH = {
1187
+ bar: { form: "button", place: "bar" },
1188
+ rail: { form: "item", place: "rail" },
1189
+ sidebar: { form: "field", place: "sidebar" }
1190
+ };
1191
+ function fixedNavigationForm(nav, form) {
1192
+ if (nav.form === form) return nav;
1193
+ return {
1194
+ ...nav,
1195
+ form,
1196
+ edge: form === "bar" ? "bottom" : "leading",
1197
+ secondaryVisible: form === "sidebar",
1198
+ search: nav.search ? { ...nav.search, ...SEARCH[form] } : null,
1199
+ expandable: form === "rail" ? nav.expandable : null,
1200
+ collapsible: form === "sidebar"
1201
+ };
1202
+ }
1203
+
1204
+ // src/kit/nav/minimize.ts
1205
+ var MINIMIZE_THRESHOLD = 24;
1206
+ var MINIMIZE_JITTER = 4;
1207
+ var createMinimize = (scrollTop = 0) => ({ minimized: false, run: 0, last: scrollTop });
1208
+ function stepMinimize(state, scrollTop) {
1209
+ const delta = scrollTop - state.last;
1210
+ state.last = scrollTop;
1211
+ if (scrollTop <= 0) {
1212
+ state.minimized = false;
1213
+ state.run = 0;
1214
+ return false;
1215
+ }
1216
+ if (delta === 0) return state.minimized;
1217
+ const along2 = Math.sign(delta) === Math.sign(state.run) || Math.abs(delta) < MINIMIZE_JITTER;
1218
+ state.run = along2 ? state.run + delta : delta;
1219
+ if (state.run >= MINIMIZE_THRESHOLD) state.minimized = true;
1220
+ else if (state.run <= -MINIMIZE_THRESHOLD) state.minimized = false;
1221
+ return state.minimized;
1222
+ }
1223
+ var clampScrollTop = (scrollTop, scrollHeight, clientHeight) => Math.min(Math.max(scrollTop, 0), Math.max(scrollHeight - clientHeight, 0));
1224
+ function expandMinimize(state) {
1225
+ state.minimized = false;
1226
+ state.run = 0;
1227
+ }
1228
+
1229
+ // src/kit/nav/morph.ts
1230
+ var NAV_MORPH_TIMING = { response: 1.06, damping: 1 };
1231
+ var NAV_MINIMIZE_TIMING = FORM_MORPH_TIMING;
1232
+ var LIFT_END = 0.12;
1233
+ var CONTRACT = [0.08, 0.38];
1234
+ var TRAVEL = [0.36, 0.56];
1235
+ var GROW = [0.52, 0.86];
1236
+ var SETTLE_START = 0.82;
1237
+ var INK_OUT = [0.08, 0.28];
1238
+ var INK_IN = [0.62, 0.86];
1239
+ var SWAP_AT = 0.46;
1240
+ var SMOOTHING = 24;
1241
+ var SEED = 8;
1242
+ var STAND_IN = 44;
1243
+ var clamp01 = (x) => Math.min(Math.max(x, 0), 1);
1244
+ var mix2 = (a, b, t) => a + (b - a) * t;
1245
+ var mixBox = (a, b, t) => ({
1246
+ x: mix2(a.x, b.x, t),
1247
+ y: mix2(a.y, b.y, t),
1248
+ width: mix2(a.width, b.width, t),
1249
+ height: mix2(a.height, b.height, t),
1250
+ radius: mix2(a.radius, b.radius, t)
1251
+ });
1252
+ var inflate = (b, d) => ({
1253
+ x: b.x - d,
1254
+ y: b.y - d,
1255
+ width: b.width + 2 * d,
1256
+ height: b.height + 2 * d,
1257
+ radius: b.radius + d
1258
+ });
1259
+ var centred = (cx, cy, size) => ({ x: cx - size / 2, y: cy - size / 2, width: size, height: size, radius: size / 2 });
1260
+ var trailingEnd = (body, size) => centred(body.x + body.width - body.height / 2, body.y + body.height / 2, size);
1261
+ var pillOrStandIn = (layout) => layout.pill ?? centred(layout.body.x + layout.body.width / 2, layout.body.y + layout.body.height / 2, STAND_IN);
1262
+ var separateSearch = (layout) => layout.form === "bar" ? layout.search : null;
1263
+ function restFrame(layout, side) {
1264
+ const search = separateSearch(layout);
1265
+ return {
1266
+ phase: "rest",
1267
+ body: layout.body,
1268
+ second: search ? { box: search, smoothing: 0 } : null,
1269
+ lens: null,
1270
+ ink: side === "from" ? { outgoing: 1, incoming: 0 } : { outgoing: 0, incoming: 1 },
1271
+ showing: side
1272
+ };
1273
+ }
1274
+ var layoutOrigin = (layout) => {
1275
+ const search = separateSearch(layout);
1276
+ return {
1277
+ body: layout.body,
1278
+ second: search ? { box: search, smoothing: 0 } : null,
1279
+ lens: { box: pillOrStandIn(layout), lift: 0 },
1280
+ ink: 1,
1281
+ lead: 0,
1282
+ hold: null
1283
+ };
1284
+ };
1285
+ var frameOrigin = (frame) => ({
1286
+ body: frame.body,
1287
+ second: frame.second,
1288
+ lens: frame.lens ?? { box: centred(frame.body.x + frame.body.width / 2, frame.body.y + frame.body.height / 2, STAND_IN), lift: 0 },
1289
+ ink: Math.max(frame.ink.outgoing, frame.ink.incoming),
1290
+ lead: CONTRACT[0],
1291
+ hold: null
1292
+ });
1293
+ function phaseAt(tau) {
1294
+ if (tau < CONTRACT[0]) return "lift";
1295
+ if (tau < TRAVEL[0]) return "contract";
1296
+ if (tau < GROW[0]) return "travel";
1297
+ return tau < SETTLE_START ? "grow" : "settle";
1298
+ }
1299
+ function lensFrame(o, to, t) {
1300
+ const tau = o.lead + (1 - o.lead) * t;
1301
+ const u = smoothstep(o.lead, LIFT_END, tau);
1302
+ const base = inflate(o.lens.box, NAV_SIZES.lens.inflate * (1 - o.lens.lift) * u);
1303
+ const lift = mix2(o.lens.lift, 1, u);
1304
+ const toPill = pillOrStandIn(to);
1305
+ const k = smoothstep(TRAVEL[0], TRAVEL[1], tau);
1306
+ const kk = o.hold ? clamp01((k - o.hold.k0) / (1 - o.hold.k0)) : k;
1307
+ const travelling = mixBox(o.hold?.box ?? base, inflate(toPill, NAV_SIZES.lens.inflate), kk);
1308
+ const c = smoothstep(CONTRACT[0], CONTRACT[1], tau);
1309
+ const g = smoothstep(GROW[0], GROW[1], tau);
1310
+ const s = smoothstep(SETTLE_START, 1, tau);
1311
+ const contracted = mixBox(o.body, travelling, c);
1312
+ const body = mixBox(contracted, to.body, g);
1313
+ let second = null;
1314
+ const budding = separateSearch(to);
1315
+ if (o.second && c < 1) {
1316
+ second = {
1317
+ box: mixBox(o.second.box, trailingEnd(contracted, 0), c),
1318
+ smoothing: mix2(o.second.smoothing, SMOOTHING, c)
1319
+ };
1320
+ } else if (budding && g > 0) {
1321
+ second = { box: mixBox(trailingEnd(body, SEED), budding, g), smoothing: SMOOTHING * (1 - g) };
1322
+ }
1323
+ return {
1324
+ phase: phaseAt(tau),
1325
+ body,
1326
+ second,
1327
+ lens: { box: mixBox(travelling, toPill, s), lift: lift * (1 - s) },
1328
+ ink: {
1329
+ outgoing: o.ink * (1 - smoothstep(INK_OUT[0], INK_OUT[1], tau)),
1330
+ incoming: smoothstep(INK_IN[0], INK_IN[1], tau)
1331
+ },
1332
+ showing: tau < SWAP_AT ? "from" : "to"
1333
+ };
1334
+ }
1335
+ function contractFrame(o, to, t) {
1336
+ const body = mixBox(o.body, to.body, t);
1337
+ const target = separateSearch(to);
1338
+ let second = null;
1339
+ if (o.second && target) {
1340
+ second = { box: mixBox(o.second.box, target, t), smoothing: mix2(o.second.smoothing, 0, t) };
1341
+ } else if (o.second) {
1342
+ second = { box: mixBox(o.second.box, trailingEnd(body, 0), t), smoothing: mix2(o.second.smoothing, SMOOTHING, t) };
1343
+ } else if (target) {
1344
+ second = { box: mixBox(trailingEnd(body, SEED), target, t), smoothing: SMOOTHING * (1 - t) };
1345
+ }
1346
+ return {
1347
+ phase: "contract",
1348
+ body,
1349
+ second,
1350
+ lens: null,
1351
+ ink: { outgoing: o.ink * (1 - smoothstep(0, 0.45, t)), incoming: smoothstep(0.55, 1, t) },
1352
+ showing: t < 0.5 ? "from" : "to"
1353
+ };
1354
+ }
1355
+ var frameFrom = (o, to, t, via) => via === "lens" ? lensFrame(o, to, t) : contractFrame(o, to, t);
1356
+ function navMorphFrame(from, to, t, via = "lens") {
1357
+ if (t <= 0) return restFrame(from, "from");
1358
+ if (t >= 1) return restFrame(to, "to");
1359
+ return frameFrom(layoutOrigin(from), to, t, via);
1360
+ }
1361
+ var createNavMorph = (at) => ({
1362
+ t: { x: 0, v: 0, target: 0 },
1363
+ from: at,
1364
+ to: at,
1365
+ via: "lens",
1366
+ pivot: null,
1367
+ pivotAt: 0
1368
+ });
1369
+ var sameForm = (a, b) => a.form === b.form && a.minimized === b.minimized;
1370
+ var atRest = (s) => s.t.v === 0 && s.t.x === s.t.target && (s.t.x === 0 || s.t.x === 1);
1371
+ function currentNavFrame(state) {
1372
+ const x = clamp01(state.t.x);
1373
+ if (x >= 1) return restFrame(state.to, "to");
1374
+ const { pivot } = state;
1375
+ if (!pivot) return navMorphFrame(state.from, state.to, x, state.via);
1376
+ if (state.pivotAt > 0 && pivot.lens) {
1377
+ const origin = layoutOrigin(state.from);
1378
+ origin.hold = { box: pivot.lens.box, k0: smoothstep(TRAVEL[0], TRAVEL[1], state.pivotAt) };
1379
+ return lensFrame(origin, state.to, x);
1380
+ }
1381
+ return frameFrom(frameOrigin(pivot), state.to, x, state.via);
1382
+ }
1383
+ function aimNavMorph(state, next, via = "lens") {
1384
+ const { t } = state;
1385
+ if (atRest(state)) {
1386
+ const current = t.x >= 1 ? state.to : state.from;
1387
+ if (sameForm(current, next)) {
1388
+ if (t.x >= 1) state.to = next;
1389
+ else state.from = next;
1390
+ state.pivot = null;
1391
+ return;
1392
+ }
1393
+ state.from = current;
1394
+ state.to = next;
1395
+ state.via = via;
1396
+ state.pivot = null;
1397
+ state.pivotAt = 0;
1398
+ t.x = 0;
1399
+ t.v = 0;
1400
+ t.target = 1;
1401
+ return;
1402
+ }
1403
+ if (!state.pivot) {
1404
+ if (sameForm(next, state.from)) {
1405
+ state.from = next;
1406
+ t.target = 0;
1407
+ return;
1408
+ }
1409
+ if (sameForm(next, state.to)) {
1410
+ state.to = next;
1411
+ t.target = 1;
1412
+ return;
1413
+ }
1414
+ const x = clamp01(t.x);
1415
+ if (via === "lens" && state.via === "lens" && x < GROW[0]) {
1416
+ if (x >= TRAVEL[0]) {
1417
+ state.pivot = currentNavFrame(state);
1418
+ state.pivotAt = x;
1419
+ }
1420
+ state.to = next;
1421
+ t.target = 1;
1422
+ return;
1423
+ }
1424
+ } else if (sameForm(next, state.to)) {
1425
+ state.to = next;
1426
+ t.target = 1;
1427
+ return;
1428
+ }
1429
+ const frozen = currentNavFrame(state);
1430
+ if (frozen.showing === "to") state.from = state.to;
1431
+ state.pivot = frozen;
1432
+ state.pivotAt = 0;
1433
+ state.to = next;
1434
+ state.via = via;
1435
+ t.x = 0;
1436
+ t.v = 0;
1437
+ t.target = 1;
1438
+ }
1439
+ function stepNavMorph(state, dt, reduceMotion = false) {
1440
+ const timing = state.via === "lens" ? NAV_MORPH_TIMING : NAV_MINIMIZE_TIMING;
1441
+ const moved = stepSpring(state.t, dt, timing.response, timing.damping, reduceMotion);
1442
+ if (!moved && state.t.x >= 1) {
1443
+ state.pivot = null;
1444
+ state.pivotAt = 0;
1445
+ }
1446
+ return moved;
1447
+ }
1448
+
923
1449
  // src/kit/response.ts
924
1450
  var LIFTING = { grow: 0.18, defocus: 1.6 };
925
1451
  var SINKING = { grow: 0.06, defocus: 1 };
@@ -948,9 +1474,14 @@ exports.MENU_CLOSED = MENU_CLOSED;
948
1474
  exports.MENU_CORNERS = MENU_CORNERS;
949
1475
  exports.MENU_DROP_BODY = MENU_DROP_BODY;
950
1476
  exports.MENU_ITEM_FILL = MENU_ITEM_FILL;
1477
+ exports.MINIMIZE_JITTER = MINIMIZE_JITTER;
1478
+ exports.MINIMIZE_THRESHOLD = MINIMIZE_THRESHOLD;
951
1479
  exports.MIN_POINTER_TARGET_DP = MIN_POINTER_TARGET_DP;
952
1480
  exports.MIN_TOUCH_TARGET_DP = MIN_TOUCH_TARGET_DP;
953
1481
  exports.MORE_ID = MORE_ID;
1482
+ exports.NAV_MINIMIZE_TIMING = NAV_MINIMIZE_TIMING;
1483
+ exports.NAV_MORPH_TIMING = NAV_MORPH_TIMING;
1484
+ exports.NAV_SIZES = NAV_SIZES;
954
1485
  exports.POINTER_INPUT = POINTER_INPUT;
955
1486
  exports.POSTURE_FLAT = POSTURE_FLAT;
956
1487
  exports.PRESS_IDLE = PRESS_IDLE;
@@ -963,17 +1494,26 @@ exports.TYPEAHEAD_SECONDS = TYPEAHEAD_SECONDS;
963
1494
  exports.WIDTH_BREAKPOINTS = WIDTH_BREAKPOINTS;
964
1495
  exports.WIDTH_CLASSES = WIDTH_CLASSES;
965
1496
  exports.adaptiveContext = adaptiveContext;
1497
+ exports.aimNavMorph = aimNavMorph;
966
1498
  exports.atomAtRest = atomAtRest;
967
1499
  exports.atomBody = atomBody;
968
1500
  exports.atomLift = atomLift;
969
1501
  exports.atomMaterial = atomMaterial;
970
1502
  exports.bottomAccessory = bottomAccessory;
1503
+ exports.cancelNavDrag = cancelNavDrag;
1504
+ exports.clampScrollTop = clampScrollTop;
971
1505
  exports.classifyWindow = classifyWindow;
972
1506
  exports.controlTargets = controlTargets;
973
1507
  exports.createControlVisual = createControlVisual;
974
1508
  exports.createFormMorph = createFormMorph;
975
1509
  exports.createGrowthState = createGrowthState;
1510
+ exports.createMinimize = createMinimize;
1511
+ exports.createNavDrag = createNavDrag;
1512
+ exports.createNavMorph = createNavMorph;
1513
+ exports.currentNavFrame = currentNavFrame;
976
1514
  exports.defaultSize = defaultSize;
1515
+ exports.expandMinimize = expandMinimize;
1516
+ exports.fixedNavigationForm = fixedNavigationForm;
977
1517
  exports.formMorph = formMorph;
978
1518
  exports.formMorphFrame = formMorphFrame;
979
1519
  exports.growthFrame = growthFrame;
@@ -996,6 +1536,10 @@ exports.menuPlacement = menuPlacement;
996
1536
  exports.menuSize = menuSize;
997
1537
  exports.menuSurfaceFrame = menuSurfaceFrame;
998
1538
  exports.minTarget = minTarget;
1539
+ exports.moveNavDrag = moveNavDrag;
1540
+ exports.navDragFrame = navDragFrame;
1541
+ exports.navLayout = navLayout;
1542
+ exports.navMorphFrame = navMorphFrame;
999
1543
  exports.navigationForm = navigationForm;
1000
1544
  exports.nestedRadius = nestedRadius;
1001
1545
  exports.paneLayout = paneLayout;
@@ -1003,9 +1547,11 @@ exports.postureFromSegments = postureFromSegments;
1003
1547
  exports.postureSplit = postureSplit;
1004
1548
  exports.presentationFor = presentationFor;
1005
1549
  exports.presentationForm = presentationForm;
1550
+ exports.pressNavDrag = pressNavDrag;
1006
1551
  exports.pressResponse = pressResponse;
1007
1552
  exports.rebaseFormMorph = rebaseFormMorph;
1008
1553
  exports.rectContains = rectContains;
1554
+ exports.releaseNavDrag = releaseNavDrag;
1009
1555
  exports.resolveA11y = resolveA11y;
1010
1556
  exports.sameA11y = sameA11y;
1011
1557
  exports.segmentOf = segmentOf;
@@ -1019,6 +1565,9 @@ exports.stepFormMorph = stepFormMorph;
1019
1565
  exports.stepGrowth = stepGrowth;
1020
1566
  exports.stepHover = stepHover;
1021
1567
  exports.stepMenu = stepMenu;
1568
+ exports.stepMinimize = stepMinimize;
1569
+ exports.stepNavDrag = stepNavDrag;
1570
+ exports.stepNavMorph = stepNavMorph;
1022
1571
  exports.stepPress = stepPress;
1023
1572
  exports.stepSpring = stepSpring;
1024
1573
  exports.supportingPane = supportingPane;