The Garage SDKs, in the open

garage_auth, garage_entitlements, garage_iap and garage_ui, moved out of
Garage-Services and Metro-Map-Maker into one public repo. MIT, one readme,
docs under docs/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013F4NWNvYcdeSgqbWMT1VQ7
This commit is contained in:
ImBenji
2026-09-23 18:49:21 +01:00
co-authored by Claude Opus 5.5
commit b269201919
117 changed files with 26944 additions and 0 deletions
+160
View File
@@ -0,0 +1,160 @@
import "dart:math" as math;
import "package:flutter/widgets.dart";
/// Where a popover should actually be placed, once the space around its
/// trigger has been measured.
///
/// Feed [targetAnchor]/[followerAnchor]/[offset] straight into a
/// [CompositedTransformFollower], and clamp the popup's own box with
/// [maxWidth]/[maxHeight].
class PopoverPlacement {
const PopoverPlacement({
required this.targetAnchor,
required this.followerAnchor,
required this.offset,
required this.maxWidth,
required this.maxHeight,
required this.openBelow,
required this.openToRight,
});
final Alignment targetAnchor;
final Alignment followerAnchor;
final Offset offset;
final double maxWidth;
final double maxHeight;
/// Which way the flip actually resolved. Callers that draw something
/// direction-dependent (a tail, a chevron) need to know.
final bool openBelow;
final bool openToRight;
}
/// Which axis a popover moves along relative to its trigger.
enum PopoverAxis {
/// Drops below the trigger (or flips above). Selects, dropdowns, menubar
/// menus - the popup is stacked under the thing that opened it.
vertical,
/// Flies out to the side of the trigger (or flips to the other side).
/// Submenus - the popup sits beside the thing that opened it.
horizontal,
}
/// Works out where a popover fits.
///
/// The rule is "keep the preferred side unless it doesn't fit and the other
/// side is genuinely better" - NOT "flip whenever the preferred side is
/// tight". A popover that flips the moment it's a pixel short would jitter
/// between sides as the window resizes, and would flip even when the opposite
/// side has less room than the side it left.
///
/// [preferredSize] is what the popup would like; when neither side can give it
/// that, the roomier side wins and the returned max dimensions clamp the popup
/// to what's actually there. Pass [Size.zero] if the size isn't known up front
/// (a shrink-wrapping menu) - the flip then falls back to a pure
/// which-side-has-more-room comparison.
PopoverPlacement resolvePopoverPlacement({
required RenderBox target,
required RenderBox overlay,
required Size preferredSize,
PopoverAxis axis = PopoverAxis.vertical,
bool preferBelow = true,
bool preferRight = true,
double gap = 0.0,
double margin = 8.0,
}) {
final targetOffset = target.localToGlobal(Offset.zero, ancestor: overlay);
final overlaySize = overlay.size;
final targetSize = target.size;
final roomBelow = overlaySize.height - targetOffset.dy - targetSize.height;
final roomAbove = targetOffset.dy;
final roomRight = overlaySize.width - targetOffset.dx;
final roomLeft = targetOffset.dx + targetSize.width;
// for a horizontal popover the sideways room is measured from the trigger's
// EDGES (it sits beside the trigger), not from its near edge the way a
// vertically-stacked popover measures its own left/right alignment room.
final roomRightOfTarget =
overlaySize.width - targetOffset.dx - targetSize.width;
final roomLeftOfTarget = targetOffset.dx;
bool fits(double room, double needed) => room >= needed + margin;
late final bool openBelow;
late final bool openToRight;
switch (axis) {
case PopoverAxis.vertical:
final needed = preferredSize.height + gap;
openBelow = preferBelow
? (fits(roomBelow, needed) || roomBelow >= roomAbove)
: !(fits(roomAbove, needed) || roomAbove >= roomBelow);
// horizontal here is just which way the popup extends from its anchor
// corner; it never sits beside the trigger, so it measures from the
// near edge.
final neededW = preferredSize.width;
openToRight = preferRight
? (fits(roomRight, neededW) || roomRight >= roomLeft)
: !(fits(roomLeft, neededW) || roomLeft >= roomRight);
case PopoverAxis.horizontal:
final needed = preferredSize.width + gap;
openToRight = preferRight
? (fits(roomRightOfTarget, needed) ||
roomRightOfTarget >= roomLeftOfTarget)
: !(fits(roomLeftOfTarget, needed) ||
roomLeftOfTarget >= roomRightOfTarget);
final neededH = preferredSize.height;
openBelow = preferBelow
? (fits(roomBelow + targetSize.height, neededH) ||
roomBelow >= roomAbove)
: !(fits(roomAbove + targetSize.height, neededH) ||
roomAbove >= roomBelow);
}
final double availableHeight;
final double availableWidth;
switch (axis) {
case PopoverAxis.vertical:
availableHeight = (openBelow ? roomBelow : roomAbove) - gap - margin;
availableWidth = (openToRight ? roomRight : roomLeft) - margin;
case PopoverAxis.horizontal:
// a side-flying popup is free to run the full height of the overlay
// from wherever it starts, so its height budget is measured from the
// trigger's own top/bottom edge rather than past it
availableHeight =
(openBelow
? roomBelow + targetSize.height
: roomAbove + targetSize.height) -
margin;
availableWidth =
(openToRight ? roomRightOfTarget : roomLeftOfTarget) - gap - margin;
}
final Alignment targetAnchor;
final Alignment followerAnchor;
final Offset resolvedOffset;
switch (axis) {
case PopoverAxis.vertical:
targetAnchor = Alignment(openToRight ? -1 : 1, openBelow ? 1 : -1);
followerAnchor = Alignment(openToRight ? -1 : 1, openBelow ? -1 : 1);
resolvedOffset = Offset(0, openBelow ? gap : -gap);
case PopoverAxis.horizontal:
targetAnchor = Alignment(openToRight ? 1 : -1, openBelow ? -1 : 1);
followerAnchor = Alignment(openToRight ? -1 : 1, openBelow ? -1 : 1);
resolvedOffset = Offset(openToRight ? gap : -gap, 0);
}
return PopoverPlacement(
targetAnchor: targetAnchor,
followerAnchor: followerAnchor,
offset: resolvedOffset,
maxWidth: math.max(0.0, availableWidth),
maxHeight: math.max(0.0, availableHeight),
openBelow: openBelow,
openToRight: openToRight,
);
}