File size: 4,267 Bytes
3e05655 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | import type { VisualRegionDefinition } from "./regions"
type RegionSet<RegionName extends string> = readonly RegionName[] | "all"
export type VisualInvariant<RegionName extends string = string> =
| { type: "required"; regions: readonly RegionName[] }
| { type: "continuous-any"; regions: readonly RegionName[] }
| { type: "unique"; regions: readonly RegionName[] }
| { type: "stable"; regions: readonly RegionName[] }
| { type: "fixed"; regions: readonly RegionName[]; tolerance?: number }
| { type: "opacity"; regions: RegionSet<RegionName>; floor?: number }
| {
type: "motion"
regions: RegionSet<RegionName>
tolerance?: number
maxReversals?: number
maxPositionReversals?: number
}
| { type: "continuity"; regions: RegionSet<RegionName> }
| { type: "label-stability"; regions: RegionSet<RegionName> }
| { type: "flow"; regions: readonly RegionName[]; overlapTolerance?: number }
| { type: "preserve-bottom-anchor" }
| { type: "acquire-bottom-anchor" }
export type VisualPlan<RegionName extends string = string> = {
regionNames?: readonly RegionName[]
invariants: readonly VisualInvariant<RegionName>[]
markerRequired?: readonly RegionName[]
perMarker?: boolean
aggregateMotion?: boolean
}
export type LegacyVisualStabilityOptions<RegionName extends string = string> = {
flow?: RegionName[]
motionTolerance?: number
opacityFloor?: number
overlapTolerance?: number
maxReversals?: number
maxPositionReversals?: number
stable?: RegionName[]
fixed?: RegionName[]
motion?: RegionName[]
unique?: RegionName[]
preserveBottomAnchor?: boolean
acquireBottomAnchor?: boolean
perMarker?: boolean
continuousAny?: RegionName[][]
required?: RegionName[]
aggregateMotion?: boolean
inferRequired?: boolean
}
export function visualPlan<const Regions extends Record<string, VisualRegionDefinition>>(
regions: Regions,
invariants: readonly VisualInvariant<Extract<keyof Regions, string>>[],
options: Omit<VisualPlan<Extract<keyof Regions, string>>, "regionNames" | "invariants"> = {},
): VisualPlan<Extract<keyof Regions, string>> {
return { ...options, regionNames: Object.keys(regions) as Extract<keyof Regions, string>[], invariants }
}
export function legacyVisualPlan<RegionName extends string>(
options: LegacyVisualStabilityOptions<RegionName> = {},
): VisualPlan<RegionName> {
const inferred =
options.inferRequired === false
? []
: [
...(options.stable ?? []),
...(options.fixed ?? []),
...(options.unique ?? []),
...(options.motion ?? []),
...(options.flow ?? []),
]
return {
perMarker: options.perMarker,
aggregateMotion: options.aggregateMotion,
markerRequired: [
...(options.required ?? []),
...(options.stable ?? []),
...(options.fixed ?? []),
...(options.unique ?? []),
...(options.motion ?? []),
...(options.flow ?? []),
],
invariants: [
{ type: "required", regions: [...(options.required ?? []), ...inferred] },
...(options.continuousAny ?? []).map(
(regions): VisualInvariant<RegionName> => ({ type: "continuous-any", regions }),
),
...(options.unique ? [{ type: "unique" as const, regions: options.unique }] : []),
...(options.stable ? [{ type: "stable" as const, regions: options.stable }] : []),
...(options.fixed
? [{ type: "fixed" as const, regions: options.fixed, tolerance: options.motionTolerance }]
: []),
{ type: "opacity", regions: "all", floor: options.opacityFloor },
{ type: "continuity", regions: "all" },
{
type: "motion",
regions: options.motion ?? "all",
tolerance: options.motionTolerance,
maxReversals: options.maxReversals,
maxPositionReversals: options.maxPositionReversals,
},
{ type: "label-stability", regions: "all" },
...(options.preserveBottomAnchor ? [{ type: "preserve-bottom-anchor" as const }] : []),
...(options.acquireBottomAnchor ? [{ type: "acquire-bottom-anchor" as const }] : []),
...(options.flow
? [{ type: "flow" as const, regions: options.flow, overlapTolerance: options.overlapTolerance }]
: []),
],
}
}
|