elements-kit 0.18.2 → 0.19.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/ui/overlay/index.css +70 -0
- package/dist/ui/overlay/index.d.mts +209 -0
- package/dist/ui/overlay/index.mjs +1001 -0
- package/dist/ui/overlay/overlay.css +332 -0
- package/dist/utilities/form-object.d.mts +96 -0
- package/dist/utilities/form-object.mjs +285 -0
- package/dist/utilities/form-object.test.d.mts +1 -0
- package/dist/utilities/form-object.test.mjs +408 -0
- package/package.json +5 -5
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
.x-overlay {
|
|
2
|
+
--overlay-constraint-top: 0px;
|
|
3
|
+
--overlay-constraint-left: 0px;
|
|
4
|
+
--overlay-constraint-width: 100vw;
|
|
5
|
+
--overlay-constraint-height: 100vh;
|
|
6
|
+
--_ct: var(--overlay-constraint-top, 0px);
|
|
7
|
+
--_cl: var(--overlay-constraint-left, 0px);
|
|
8
|
+
--_cw: var(--overlay-constraint-width, 100vw);
|
|
9
|
+
--_ch: var(--overlay-constraint-height, 100vh);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@supports (height: 1svh) {
|
|
13
|
+
.x-overlay {
|
|
14
|
+
--overlay-constraint-width: 100svw;
|
|
15
|
+
--overlay-constraint-height: 100svh;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.x-overlay {
|
|
20
|
+
top: var(--_ct);
|
|
21
|
+
left: var(--_cl);
|
|
22
|
+
translate: calc(-50% + var(--_ex, 0px) + var(--overlay-dx, 0px) +
|
|
23
|
+
clamp(50%,
|
|
24
|
+
var(--overlay-x, calc(var(--_cw) / 2)),
|
|
25
|
+
calc(-50% + var(--_cw))))
|
|
26
|
+
calc(-50% + var(--_ey, 0px) + var(--overlay-dy, 0px) +
|
|
27
|
+
clamp(50%,
|
|
28
|
+
var(--overlay-y, calc(var(--_ch) / 2)),
|
|
29
|
+
calc(-50% + var(--_ch))));
|
|
30
|
+
--overlay-width: 480px;
|
|
31
|
+
width: var(--overlay-w, var(--overlay-width));
|
|
32
|
+
max-width: var(--_cw);
|
|
33
|
+
height: var(--overlay-h, fit-content);
|
|
34
|
+
max-height: var(--_ch);
|
|
35
|
+
--overlay-duration: .3s;
|
|
36
|
+
--overlay-easing: cubic-bezier(.32, .72, 0, 1);
|
|
37
|
+
transition: top var(--overlay-duration) var(--overlay-easing),
|
|
38
|
+
left var(--overlay-duration) var(--overlay-easing),
|
|
39
|
+
translate var(--overlay-duration) var(--overlay-easing),
|
|
40
|
+
width var(--overlay-duration) var(--overlay-easing),
|
|
41
|
+
height var(--overlay-duration) var(--overlay-easing);
|
|
42
|
+
grid-template-rows: minmax(0, 1fr);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.x-overlay:not([open], :popover-open) {
|
|
46
|
+
pointer-events: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.x-overlay:where([popover]) {
|
|
50
|
+
display: none;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.x-overlay:where([open], :popover-open) {
|
|
54
|
+
display: grid;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@supports not (translate: 0px) {
|
|
58
|
+
.x-overlay {
|
|
59
|
+
top: auto;
|
|
60
|
+
left: auto;
|
|
61
|
+
margin: auto;
|
|
62
|
+
inset: 0;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@supports (interpolate-size: allow-keywords) {
|
|
67
|
+
.x-overlay {
|
|
68
|
+
interpolate-size: allow-keywords;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
//#region src/ui/overlay/constrain.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The overlay constraint — the rect an `.x-overlay` lives in. Every
|
|
4
|
+
* location clamp and gesture bound derives from the
|
|
5
|
+
* `--overlay-constraint-top/-left/-width/-height` variables (declared in
|
|
6
|
+
* index.css with viewport defaults). This module is the JS side of that
|
|
7
|
+
* interface: syncing a rect into the variables and resolving them back to
|
|
8
|
+
* pixels for the gesture bounds.
|
|
9
|
+
*/
|
|
10
|
+
interface OverlayConstraint {
|
|
11
|
+
dispose(): void;
|
|
12
|
+
[Symbol.dispose](): void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A reactive source of the constraint rect — the shape `createElementRect`
|
|
16
|
+
* returns, so an `Element` works directly and custom rects (a non-element
|
|
17
|
+
* region, a virtual anchor) can be supplied too.
|
|
18
|
+
*/
|
|
19
|
+
interface ConstraintRect {
|
|
20
|
+
top(): number;
|
|
21
|
+
left(): number;
|
|
22
|
+
width(): number;
|
|
23
|
+
height(): number;
|
|
24
|
+
}
|
|
25
|
+
/** Resolves the constraint rect every gesture bound derives from. */
|
|
26
|
+
declare function resolveConstraint(overlay: HTMLElement): {
|
|
27
|
+
top: number;
|
|
28
|
+
left: number;
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Confines an overlay to a rect source by syncing it into the
|
|
34
|
+
* `--overlay-constraint-*` variables. Pass an `Element` (observed via
|
|
35
|
+
* `ResizeObserver` through `createElementRect`) or a custom
|
|
36
|
+
* {@link ConstraintRect}. Every location clamp and gesture bound derives
|
|
37
|
+
* from those variables, so the overlay re-clamps when the rect changes.
|
|
38
|
+
*
|
|
39
|
+
* Caveat: `ResizeObserver` fires on size changes — a container that moves
|
|
40
|
+
* without resizing (e.g. page scroll) does not retrigger the sync.
|
|
41
|
+
*
|
|
42
|
+
* Registers its cleanup with the current scope (`onCleanup`) and also
|
|
43
|
+
* returns it as `dispose` / `Symbol.dispose`; disposing removes the
|
|
44
|
+
* variables, restoring the viewport constraint.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* import { constrainOverlay } from "elements-kit/ui/overlay";
|
|
49
|
+
*
|
|
50
|
+
* const panel = document.querySelector("dialog.x-overlay")!;
|
|
51
|
+
* constrainOverlay(panel, document.querySelector("main")!);
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare function constrainOverlay(overlay: HTMLElement, source: Element | ConstraintRect): OverlayConstraint;
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/ui/overlay/resize-strategy.d.ts
|
|
57
|
+
/**
|
|
58
|
+
* Resize strategies — pluggable policy for a resize drag's live bounds
|
|
59
|
+
* and resting size. Pure (no DOM); the gesture injects the context.
|
|
60
|
+
* Built-ins: `freeResize` (default), `detents`.
|
|
61
|
+
*/
|
|
62
|
+
/** How far (ms) a release velocity is projected when picking a rest. */
|
|
63
|
+
declare const PROJECTION_MS = 160;
|
|
64
|
+
/** Clamp `value` into `[min, max]`. */
|
|
65
|
+
declare function clamp(value: number, min: number, max: number): number;
|
|
66
|
+
/**
|
|
67
|
+
* The release context a `ResizeStrategy` decides against. The gesture
|
|
68
|
+
* builds it per drag; `resolve` turns a step value into pixels.
|
|
69
|
+
*/
|
|
70
|
+
interface ResizeContext {
|
|
71
|
+
/** Dragged size along the axis (px, before clamping). */
|
|
72
|
+
size: number;
|
|
73
|
+
/** Size at the gesture's start (px). */
|
|
74
|
+
startSize: number;
|
|
75
|
+
/** Release velocity along the axis (px/ms; positive = shrinking). */
|
|
76
|
+
velocity: number;
|
|
77
|
+
/** Resize axis. */
|
|
78
|
+
axis: "width" | "height";
|
|
79
|
+
/** Hard room the surface may occupy on the axis (px). */
|
|
80
|
+
min: number;
|
|
81
|
+
max: number;
|
|
82
|
+
/** Whether a drag/flick past the minimum may dismiss. */
|
|
83
|
+
dismissible: boolean;
|
|
84
|
+
/** Release velocity (px/ms) past which a sub-minimum release dismisses. */
|
|
85
|
+
velocityThreshold: number;
|
|
86
|
+
/** Resolves a step to px — a number is a fraction of the constraint
|
|
87
|
+
* along the axis; a string is any CSS length. */
|
|
88
|
+
resolve(value: number | string): number;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Decides where a resize drag rests. The gesture calls `bounds()` for the
|
|
92
|
+
* live rubber-band and `rest()` on release (the resting size, or `null`
|
|
93
|
+
* to dismiss). Built-ins: `freeResize` (default), `detents`.
|
|
94
|
+
*/
|
|
95
|
+
interface ResizeStrategy {
|
|
96
|
+
/** Soft `[lo, hi]` bounds for the live drag — rubber-band past these.
|
|
97
|
+
* Defaults to the hard room when omitted. */
|
|
98
|
+
bounds?(ctx: ResizeContext): [number, number];
|
|
99
|
+
/** Resting size (px) on release, or `null` to dismiss. */
|
|
100
|
+
rest(ctx: ResizeContext): number | null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Picks the index of the detent closest to the released size, projected
|
|
104
|
+
* along the release velocity (px/ms, positive = shrinking). Returns `-1`
|
|
105
|
+
* when the gesture should dismiss instead.
|
|
106
|
+
*/
|
|
107
|
+
declare function closestDetent(sizePx: number, detentsPx: readonly number[], velocityPxPerMs?: number, dismissible?: boolean, velocityThreshold?: number): number;
|
|
108
|
+
/**
|
|
109
|
+
* Free resize: drag to any size within the room; a flick or shrink past
|
|
110
|
+
* the minimum dismisses. The default strategy.
|
|
111
|
+
*/
|
|
112
|
+
declare function freeResize(opts?: {
|
|
113
|
+
min?: number;
|
|
114
|
+
}): ResizeStrategy;
|
|
115
|
+
/**
|
|
116
|
+
* Snap to discrete steps — each a fraction of the constraint along the
|
|
117
|
+
* axis (number `0–1`) or a CSS length (string). Flick-aware; shrinking
|
|
118
|
+
* past the smallest step dismisses.
|
|
119
|
+
*/
|
|
120
|
+
declare function detents(steps: readonly (number | string)[]): ResizeStrategy;
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/ui/overlay/gestures.d.ts
|
|
123
|
+
interface OverlayGestureOptions {
|
|
124
|
+
/** How a resize drag rests. Default: `freeResize()`. */
|
|
125
|
+
resize?: ResizeStrategy;
|
|
126
|
+
/** Allow a drag/flick past the minimum to close. Default `true`. */
|
|
127
|
+
dismissible?: boolean;
|
|
128
|
+
/** Release velocity (px/ms, shrinking) that dismisses. Default `0.5`. */
|
|
129
|
+
velocityThreshold?: number;
|
|
130
|
+
}
|
|
131
|
+
interface OverlayGestures {
|
|
132
|
+
/** Resize to a size (px) along the resize axis — animated by CSS. */
|
|
133
|
+
resize(size: number): void;
|
|
134
|
+
dispose(): void;
|
|
135
|
+
[Symbol.dispose](): void;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Opt-in pointer gestures for `.x-overlay`, dispatched by the gesture
|
|
139
|
+
* attributes (structure stays in markup; policy is options):
|
|
140
|
+
*
|
|
141
|
+
* - An edge word (`block-*` / `inline-*`) is a whole-surface size drag
|
|
142
|
+
* along that axis — block drags the height (sheets), inline the width
|
|
143
|
+
* (drawers), `:dir(rtl)` flips the inline sign. The side names the
|
|
144
|
+
* handle; the opposite edge stays put.
|
|
145
|
+
* - A corner word (`start-start` / … — block side first) is a desktop-
|
|
146
|
+
* window resize from a square zone at that corner, anchored at the
|
|
147
|
+
* opposite corner so the surface never grows past the constraint. The
|
|
148
|
+
* width follows the `resize` strategy; the height is a free clamp.
|
|
149
|
+
* - `data-draggable` moves the surface in x/y from the top strip,
|
|
150
|
+
* rubber-banding at the edges; flinging it off the constraint dismisses
|
|
151
|
+
* (when `dismissible`).
|
|
152
|
+
*
|
|
153
|
+
* Layered for testability: `gesture-model` owns the pure mode reducers +
|
|
154
|
+
* math, and `overlay-dom` owns all DOM contact (pointer plumbing + channel
|
|
155
|
+
* I/O). This function is the wiring — it picks a pure `Session` from
|
|
156
|
+
* `detectEngagement` and adapts it to the recognizer through the io.
|
|
157
|
+
* The `resize` strategy (`freeResize` by default, or `detents`) decides the
|
|
158
|
+
* rested size, written to the public `--overlay-w`/`--overlay-h` channels;
|
|
159
|
+
* CSS renders and animates them. JS never touches `translate`/`top`/`left`.
|
|
160
|
+
*
|
|
161
|
+
* Registers its cleanup with the current scope (`onCleanup`) and also
|
|
162
|
+
* returns it as `dispose` / `Symbol.dispose`.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```ts
|
|
166
|
+
* import { createOverlayGestures, detents } from "elements-kit/ui/overlay";
|
|
167
|
+
*
|
|
168
|
+
* const el = document.querySelector("dialog.x-overlay")!;
|
|
169
|
+
* createOverlayGestures(el, { resize: detents([0.25, 0.6, 0.9]) });
|
|
170
|
+
* el.addEventListener("resizechange", (e) => console.log(e.detail));
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
declare function createOverlayGestures(overlay: HTMLElement, options?: OverlayGestureOptions): OverlayGestures;
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/ui/overlay/overlay.d.ts
|
|
176
|
+
interface OverlayConfig extends OverlayGestureOptions {
|
|
177
|
+
/** Confine the overlay to an element's rect (or a custom
|
|
178
|
+
* {@link ConstraintRect}). Omit for the default viewport constraint. */
|
|
179
|
+
constrain?: Element | ConstraintRect;
|
|
180
|
+
}
|
|
181
|
+
interface OverlayController {
|
|
182
|
+
/** Resize to a size (px) along the resize axis — animated by CSS. */
|
|
183
|
+
resize(size: number): void;
|
|
184
|
+
dispose(): void;
|
|
185
|
+
[Symbol.dispose](): void;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* One-call wiring for an interactive `.x-overlay`: optional constraint
|
|
189
|
+
* sync + pointer gestures, under a single disposable. Structure stays in
|
|
190
|
+
* markup (`data-resize` / `data-draggable`); policy is config.
|
|
191
|
+
*
|
|
192
|
+
* Registers its cleanup with the current scope (`onCleanup`) and also
|
|
193
|
+
* returns it as `dispose` / `Symbol.dispose`.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* import { overlay, detents } from "elements-kit/ui/overlay";
|
|
198
|
+
*
|
|
199
|
+
* const el = document.querySelector("dialog.x-overlay")!;
|
|
200
|
+
* const o = overlay(el, {
|
|
201
|
+
* constrain: document.querySelector("main")!,
|
|
202
|
+
* resize: detents([0.25, 0.6, 0.9]),
|
|
203
|
+
* onResize: (size) => console.log(size),
|
|
204
|
+
* });
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
declare function overlay(el: HTMLElement, config?: OverlayConfig): OverlayController;
|
|
208
|
+
//#endregion
|
|
209
|
+
export { ConstraintRect, OverlayConfig, OverlayConstraint, OverlayController, OverlayGestureOptions, OverlayGestures, PROJECTION_MS, ResizeContext, ResizeStrategy, clamp, closestDetent, constrainOverlay, createOverlayGestures, detents, freeResize, overlay, resolveConstraint };
|