@estiva-app/ui 0.12.5 → 0.12.7

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/src/fit.test.ts DELETED
@@ -1,101 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { fitMenu } from './fit'
3
-
4
- /**
5
- * The last hand-written geometry in the package, and its one remaining
6
- * caller's.
7
- *
8
- * These were the Menu shell's tests (Katerina, 2026-09-03: the identity menu
9
- * vanished under a z-indexed panel header, and a reply menu's highlight
10
- * submenu was cut off by the right screen edge — its hand-rolled flip read a
11
- * ref that was never attached, so it measured nothing). Stage 4 gave that job
12
- * to Floating UI, so the `clampBox` and `fitSubmenu` blocks are gone with the
13
- * functions, and what is left is `fitMenu` as **`ChipInput`** uses it: a
14
- * suggestion list under a text field, capped at 240.
15
- *
16
- * When `ChipInput` moves onto Base UI's `Combobox` at stage 5, this file and
17
- * `fit.ts` go together.
18
- */
19
- const viewport = { width: 1280, height: 800 }
20
- /** ChipInput's call: 48px rows, and the old `max-h-[240px]`. */
21
- const fitList = (args: Omit<Parameters<typeof fitMenu>[0], 'cap'>) => fitMenu({ ...args, cap: 240 })
22
-
23
- describe('fitMenu, as ChipInput calls it', () => {
24
- it('leaves a comfortable list exactly where the field put it', () => {
25
- const fit = fitList({
26
- anchor: { left: 100, top: 200, bottom: 232 },
27
- menu: { width: 240, contentHeight: 144 },
28
- viewport,
29
- })
30
- expect(fit.left).toBe(100)
31
- expect(fit.top).toBe(236)
32
- expect(fit.bottom).toBeUndefined()
33
- })
34
-
35
- it('clamps a list that would run off the right edge', () => {
36
- const fit = fitList({
37
- anchor: { left: 1200, top: 200, bottom: 232 },
38
- menu: { width: 340, contentHeight: 144 },
39
- viewport,
40
- })
41
- // 1280 - 340 - 8 margin: fully on screen, margin kept.
42
- expect(fit.left).toBe(932)
43
- })
44
-
45
- it('never pushes a list past the LEFT edge either', () => {
46
- const fit = fitList({
47
- anchor: { left: -20, top: 200, bottom: 232 },
48
- menu: { width: 240, contentHeight: 144 },
49
- viewport,
50
- })
51
- expect(fit.left).toBe(8)
52
- })
53
-
54
- it('opens upward when the room above is better — the field low on the screen', () => {
55
- const fit = fitList({
56
- anchor: { left: 100, top: 700, bottom: 732 },
57
- menu: { width: 240, contentHeight: 480 },
58
- viewport,
59
- })
60
- expect(fit.top).toBeUndefined()
61
- expect(fit.bottom).toBe(800 - 700 + 4) // anchored to the field's top
62
- expect(fit.maxHeight).toBe(240) // still capped
63
- })
64
-
65
- it('stays below when the room below is bad but the room above is worse', () => {
66
- const fit = fitList({
67
- anchor: { left: 100, top: 60, bottom: 92 },
68
- menu: { width: 240, contentHeight: 480 },
69
- viewport,
70
- })
71
- expect(fit.top).toBe(96)
72
- expect(fit.maxHeight).toBe(240)
73
- })
74
-
75
- it('caps a long list at 240 with plenty of room — the old max-h-[240px]', () => {
76
- const fit = fitList({
77
- anchor: { left: 100, top: 100, bottom: 132 },
78
- menu: { width: 240, contentHeight: 900 },
79
- viewport,
80
- })
81
- expect(fit.maxHeight).toBe(240)
82
- })
83
-
84
- it('keeps a 120px floor in a cramped corner — scrollable beats invisible', () => {
85
- const fit = fitList({
86
- anchor: { left: 100, top: 740, bottom: 772 },
87
- menu: { width: 240, contentHeight: 480 },
88
- viewport,
89
- })
90
- expect(fit.maxHeight).toBeGreaterThanOrEqual(120)
91
- })
92
-
93
- it('with no cap, a tall list stands at its full height when the room is there', () => {
94
- const fit = fitMenu({
95
- anchor: { left: 1100, top: 20, bottom: 56 },
96
- menu: { width: 288, contentHeight: 450 },
97
- viewport: { width: 1440, height: 900 },
98
- })
99
- expect(fit.maxHeight).toBeGreaterThanOrEqual(450)
100
- })
101
- })
package/src/fit.ts DELETED
@@ -1,50 +0,0 @@
1
- /**
2
- * The last of the hand-written viewport geometry.
3
- *
4
- * This module existed because the same failure kept recurring one floating
5
- * surface at a time: the files-panel picker was cut off at the right edge
6
- * (Select, 2026-09-01), then a reply menu's highlight submenu was cut the
7
- * same way and the identity menu vanished under a z-indexed header
8
- * (2026-09-03). Stage 4 handed that job to Floating UI, so `Menu`, `MenuSub`
9
- * and `Select` no longer call any of it, and `clampBox` and `fitSubmenu` are
10
- * gone with them.
11
- *
12
- * **`fitMenu` survives for exactly one caller: `ChipInput`**, whose suggestion
13
- * list is still hand-placed. It goes when `ChipInput` moves onto Base UI's
14
- * `Combobox` at stage 5, and this file goes with it. `PLAN.md` §6 said the
15
- * geometry had three callers and could be deleted at stage 4; it had four.
16
- */
17
- const MARGIN = 8
18
- const GAP = 4
19
-
20
- /**
21
- * Where a list of this size goes, given its anchor and the viewport.
22
- *
23
- * Left is clamped inside the viewport with an 8px margin. Height is capped at
24
- * `cap` — `ChipInput` passes 240, its old `max-h-[240px]` — but never taller
25
- * than the room there is; when the room below the anchor is smaller than both
26
- * the content and the room above, the list opens UPWARD (anchored to the
27
- * trigger's top via `bottom`). The 120px floor keeps it usable even in a
28
- * cramped corner — scrollable beats invisible.
29
- */
30
- export function fitMenu({
31
- anchor,
32
- menu,
33
- viewport,
34
- cap = Number.POSITIVE_INFINITY,
35
- }: {
36
- anchor: { left: number; top: number; bottom: number }
37
- menu: { width: number; contentHeight: number }
38
- viewport: { width: number; height: number }
39
- /** Tallest the menu may stand even with room to spare. Absent: the room is the only limit. */
40
- cap?: number
41
- }): { left: number; top?: number; bottom?: number; maxHeight: number } {
42
- const left = Math.max(MARGIN, Math.min(anchor.left, viewport.width - menu.width - MARGIN))
43
- const below = viewport.height - anchor.bottom - GAP - MARGIN
44
- const above = anchor.top - GAP - MARGIN
45
- const openUp = below < Math.min(menu.contentHeight, cap) && above > below
46
- const maxHeight = Math.max(Math.min(cap, openUp ? above : below), 120)
47
- return openUp
48
- ? { left, bottom: viewport.height - anchor.top + GAP, maxHeight }
49
- : { left, top: anchor.bottom + GAP, maxHeight }
50
- }