@mantine/hooks 9.1.0 → 9.2.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/cjs/index.cjs +2 -0
- package/cjs/use-drag/use-drag.cjs +266 -0
- package/cjs/use-drag/use-drag.cjs.map +1 -0
- package/cjs/use-focus-return/use-focus-return.cjs +7 -1
- package/cjs/use-focus-return/use-focus-return.cjs.map +1 -1
- package/cjs/use-mask/use-mask.cjs +33 -4
- package/cjs/use-mask/use-mask.cjs.map +1 -1
- package/cjs/use-radial-move/use-radial-move.cjs +12 -4
- package/cjs/use-radial-move/use-radial-move.cjs.map +1 -1
- package/esm/index.mjs +2 -1
- package/esm/use-drag/use-drag.mjs +266 -0
- package/esm/use-drag/use-drag.mjs.map +1 -0
- package/esm/use-focus-return/use-focus-return.mjs +7 -1
- package/esm/use-focus-return/use-focus-return.mjs.map +1 -1
- package/esm/use-mask/use-mask.mjs +33 -4
- package/esm/use-mask/use-mask.mjs.map +1 -1
- package/esm/use-radial-move/use-radial-move.mjs +13 -5
- package/esm/use-radial-move/use-radial-move.mjs.map +1 -1
- package/lib/index.d.mts +2 -0
- package/lib/index.d.ts +2 -0
- package/lib/use-drag/use-drag.d.ts +60 -0
- package/package.json +1 -1
package/cjs/index.cjs
CHANGED
|
@@ -87,6 +87,7 @@ const require_use_collapse = require("./use-collapse/use-collapse.cjs");
|
|
|
87
87
|
const require_use_horizontal_collapse = require("./use-collapse/use-horizontal-collapse.cjs");
|
|
88
88
|
const require_use_mask = require("./use-mask/use-mask.cjs");
|
|
89
89
|
const require_use_roving_index = require("./use-roving-index/use-roving-index.cjs");
|
|
90
|
+
const require_use_drag = require("./use-drag/use-drag.cjs");
|
|
90
91
|
exports.assignRef = require_use_merged_ref.assignRef;
|
|
91
92
|
exports.clamp = require_clamp.clamp;
|
|
92
93
|
exports.clampUseMovePosition = require_use_move.clampUseMovePosition;
|
|
@@ -117,6 +118,7 @@ exports.useDidUpdate = require_use_did_update.useDidUpdate;
|
|
|
117
118
|
exports.useDisclosure = require_use_disclosure.useDisclosure;
|
|
118
119
|
exports.useDocumentTitle = require_use_document_title.useDocumentTitle;
|
|
119
120
|
exports.useDocumentVisibility = require_use_document_visibility.useDocumentVisibility;
|
|
121
|
+
exports.useDrag = require_use_drag.useDrag;
|
|
120
122
|
exports.useElementSize = require_use_resize_observer.useElementSize;
|
|
121
123
|
exports.useEventListener = require_use_event_listener.useEventListener;
|
|
122
124
|
exports.useEyeDropper = require_use_eye_dropper.useEyeDropper;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
let react = require("react");
|
|
3
|
+
//#region packages/@mantine/hooks/src/use-drag/use-drag.ts
|
|
4
|
+
const VELOCITY_DECAY_MS = 100;
|
|
5
|
+
function sign(n) {
|
|
6
|
+
if (n > 0) return 1;
|
|
7
|
+
if (n < 0) return -1;
|
|
8
|
+
return 0;
|
|
9
|
+
}
|
|
10
|
+
function getThresholdVector(threshold) {
|
|
11
|
+
const t = threshold ?? 0;
|
|
12
|
+
if (typeof t === "number") return [t, t];
|
|
13
|
+
return t;
|
|
14
|
+
}
|
|
15
|
+
function createInitialState() {
|
|
16
|
+
return {
|
|
17
|
+
isActive: false,
|
|
18
|
+
pointerId: -1,
|
|
19
|
+
startXY: [0, 0],
|
|
20
|
+
prevXY: [0, 0],
|
|
21
|
+
startTimestamp: 0,
|
|
22
|
+
prevTimestamp: 0,
|
|
23
|
+
thresholdMet: false,
|
|
24
|
+
firstFired: false,
|
|
25
|
+
lockedAxis: null,
|
|
26
|
+
canceled: false,
|
|
27
|
+
lastVelocity: [0, 0]
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function useDrag(handler, options = {}) {
|
|
31
|
+
const [active, setActive] = (0, react.useState)(false);
|
|
32
|
+
const handlerRef = (0, react.useRef)(handler);
|
|
33
|
+
handlerRef.current = handler;
|
|
34
|
+
const optionsRef = (0, react.useRef)(options);
|
|
35
|
+
optionsRef.current = options;
|
|
36
|
+
const stateRef = (0, react.useRef)(createInitialState());
|
|
37
|
+
const documentControllerRef = (0, react.useRef)(null);
|
|
38
|
+
return {
|
|
39
|
+
ref: (0, react.useCallback)((node) => {
|
|
40
|
+
if (!node) return;
|
|
41
|
+
const elementController = new AbortController();
|
|
42
|
+
const applyAxisConstraint = (v) => {
|
|
43
|
+
const opts = optionsRef.current;
|
|
44
|
+
const s = stateRef.current;
|
|
45
|
+
if (opts.axis === "x") return [v[0], 0];
|
|
46
|
+
if (opts.axis === "y") return [0, v[1]];
|
|
47
|
+
if (opts.axis === "lock") {
|
|
48
|
+
if (s.lockedAxis === null) {
|
|
49
|
+
const t = opts.axisThreshold ?? 1;
|
|
50
|
+
if (Math.abs(v[0]) > t || Math.abs(v[1]) > t) s.lockedAxis = Math.abs(v[0]) >= Math.abs(v[1]) ? "x" : "y";
|
|
51
|
+
}
|
|
52
|
+
if (s.lockedAxis === "x") return [v[0], 0];
|
|
53
|
+
if (s.lockedAxis === "y") return [0, v[1]];
|
|
54
|
+
}
|
|
55
|
+
return v;
|
|
56
|
+
};
|
|
57
|
+
const resetDrag = () => {
|
|
58
|
+
const s = stateRef.current;
|
|
59
|
+
s.isActive = false;
|
|
60
|
+
s.pointerId = -1;
|
|
61
|
+
s.thresholdMet = false;
|
|
62
|
+
s.firstFired = false;
|
|
63
|
+
s.lockedAxis = null;
|
|
64
|
+
s.canceled = false;
|
|
65
|
+
setActive(false);
|
|
66
|
+
document.body.style.userSelect = "";
|
|
67
|
+
document.body.style.webkitUserSelect = "";
|
|
68
|
+
documentControllerRef.current?.abort();
|
|
69
|
+
documentControllerRef.current = null;
|
|
70
|
+
};
|
|
71
|
+
const cancel = () => {
|
|
72
|
+
if (stateRef.current.isActive) {
|
|
73
|
+
stateRef.current.canceled = true;
|
|
74
|
+
resetDrag();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const activateDrag = () => {
|
|
78
|
+
setActive(true);
|
|
79
|
+
document.body.style.userSelect = "none";
|
|
80
|
+
document.body.style.webkitUserSelect = "none";
|
|
81
|
+
};
|
|
82
|
+
const onPointerDown = (event) => {
|
|
83
|
+
if (optionsRef.current.enabled === false) return;
|
|
84
|
+
if (event.button !== 0) return;
|
|
85
|
+
if (stateRef.current.isActive) return;
|
|
86
|
+
const s = stateRef.current;
|
|
87
|
+
s.isActive = true;
|
|
88
|
+
s.pointerId = event.pointerId;
|
|
89
|
+
s.startXY = [event.clientX, event.clientY];
|
|
90
|
+
s.prevXY = [event.clientX, event.clientY];
|
|
91
|
+
s.startTimestamp = event.timeStamp;
|
|
92
|
+
s.prevTimestamp = event.timeStamp;
|
|
93
|
+
s.thresholdMet = false;
|
|
94
|
+
s.firstFired = false;
|
|
95
|
+
s.lockedAxis = null;
|
|
96
|
+
s.canceled = false;
|
|
97
|
+
s.lastVelocity = [0, 0];
|
|
98
|
+
const [tx, ty] = getThresholdVector(optionsRef.current.threshold);
|
|
99
|
+
if (tx === 0 && ty === 0) {
|
|
100
|
+
s.thresholdMet = true;
|
|
101
|
+
s.firstFired = true;
|
|
102
|
+
activateDrag();
|
|
103
|
+
handlerRef.current({
|
|
104
|
+
xy: [event.clientX, event.clientY],
|
|
105
|
+
initial: [event.clientX, event.clientY],
|
|
106
|
+
movement: [0, 0],
|
|
107
|
+
delta: [0, 0],
|
|
108
|
+
distance: [0, 0],
|
|
109
|
+
direction: [0, 0],
|
|
110
|
+
velocity: [0, 0],
|
|
111
|
+
elapsedTime: 0,
|
|
112
|
+
first: true,
|
|
113
|
+
last: false,
|
|
114
|
+
active: true,
|
|
115
|
+
tap: false,
|
|
116
|
+
canceled: false,
|
|
117
|
+
cancel,
|
|
118
|
+
event
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
documentControllerRef.current?.abort();
|
|
122
|
+
documentControllerRef.current = new AbortController();
|
|
123
|
+
const sig = documentControllerRef.current.signal;
|
|
124
|
+
document.addEventListener("pointermove", onPointerMove, { signal: sig });
|
|
125
|
+
document.addEventListener("pointerup", onPointerUp, { signal: sig });
|
|
126
|
+
document.addEventListener("pointercancel", onPointerCancel, { signal: sig });
|
|
127
|
+
};
|
|
128
|
+
const onPointerMove = (event) => {
|
|
129
|
+
const s = stateRef.current;
|
|
130
|
+
if (!s.isActive || event.pointerId !== s.pointerId) return;
|
|
131
|
+
const rawMovement = [event.clientX - s.startXY[0], event.clientY - s.startXY[1]];
|
|
132
|
+
if (!s.thresholdMet) {
|
|
133
|
+
const [tx, ty] = getThresholdVector(optionsRef.current.threshold);
|
|
134
|
+
if (Math.abs(rawMovement[0]) < tx && Math.abs(rawMovement[1]) < ty) {
|
|
135
|
+
s.prevXY = [event.clientX, event.clientY];
|
|
136
|
+
s.prevTimestamp = event.timeStamp;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
s.thresholdMet = true;
|
|
140
|
+
activateDrag();
|
|
141
|
+
}
|
|
142
|
+
const movement = applyAxisConstraint(rawMovement);
|
|
143
|
+
const delta = applyAxisConstraint([event.clientX - s.prevXY[0], event.clientY - s.prevXY[1]]);
|
|
144
|
+
const timeDelta = event.timeStamp - s.prevTimestamp;
|
|
145
|
+
const velocity = timeDelta > 0 ? [Math.abs(delta[0]) / timeDelta, Math.abs(delta[1]) / timeDelta] : s.lastVelocity;
|
|
146
|
+
s.lastVelocity = velocity;
|
|
147
|
+
const isFirst = !s.firstFired;
|
|
148
|
+
s.firstFired = true;
|
|
149
|
+
s.prevXY = [event.clientX, event.clientY];
|
|
150
|
+
s.prevTimestamp = event.timeStamp;
|
|
151
|
+
handlerRef.current({
|
|
152
|
+
xy: [event.clientX, event.clientY],
|
|
153
|
+
initial: [...s.startXY],
|
|
154
|
+
movement,
|
|
155
|
+
delta,
|
|
156
|
+
distance: [Math.abs(movement[0]), Math.abs(movement[1])],
|
|
157
|
+
direction: [sign(delta[0]), sign(delta[1])],
|
|
158
|
+
velocity,
|
|
159
|
+
elapsedTime: event.timeStamp - s.startTimestamp,
|
|
160
|
+
first: isFirst,
|
|
161
|
+
last: false,
|
|
162
|
+
active: true,
|
|
163
|
+
tap: false,
|
|
164
|
+
canceled: false,
|
|
165
|
+
cancel,
|
|
166
|
+
event
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
const onPointerUp = (event) => {
|
|
170
|
+
const s = stateRef.current;
|
|
171
|
+
if (!s.isActive || event.pointerId !== s.pointerId) return;
|
|
172
|
+
const opts = optionsRef.current;
|
|
173
|
+
if (!s.thresholdMet) {
|
|
174
|
+
if (opts.filterTaps) {
|
|
175
|
+
const mov = applyAxisConstraint([event.clientX - s.startXY[0], event.clientY - s.startXY[1]]);
|
|
176
|
+
const dist = [Math.abs(mov[0]), Math.abs(mov[1])];
|
|
177
|
+
const isTap = Math.max(dist[0], dist[1]) < (opts.tapThreshold ?? 3);
|
|
178
|
+
handlerRef.current({
|
|
179
|
+
xy: [event.clientX, event.clientY],
|
|
180
|
+
initial: [...s.startXY],
|
|
181
|
+
movement: mov,
|
|
182
|
+
delta: mov,
|
|
183
|
+
distance: dist,
|
|
184
|
+
direction: [sign(mov[0]), sign(mov[1])],
|
|
185
|
+
velocity: [0, 0],
|
|
186
|
+
elapsedTime: event.timeStamp - s.startTimestamp,
|
|
187
|
+
first: true,
|
|
188
|
+
last: true,
|
|
189
|
+
active: false,
|
|
190
|
+
tap: isTap,
|
|
191
|
+
canceled: false,
|
|
192
|
+
cancel,
|
|
193
|
+
event
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
resetDrag();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const movement = applyAxisConstraint([event.clientX - s.startXY[0], event.clientY - s.startXY[1]]);
|
|
200
|
+
const distance = [Math.abs(movement[0]), Math.abs(movement[1])];
|
|
201
|
+
const delta = applyAxisConstraint([event.clientX - s.prevXY[0], event.clientY - s.prevXY[1]]);
|
|
202
|
+
const velocity = event.timeStamp - s.prevTimestamp > VELOCITY_DECAY_MS ? [0, 0] : s.lastVelocity;
|
|
203
|
+
const maxDistance = Math.max(distance[0], distance[1]);
|
|
204
|
+
const tap = opts.filterTaps === true && maxDistance < (opts.tapThreshold ?? 3);
|
|
205
|
+
handlerRef.current({
|
|
206
|
+
xy: [event.clientX, event.clientY],
|
|
207
|
+
initial: [...s.startXY],
|
|
208
|
+
movement,
|
|
209
|
+
delta,
|
|
210
|
+
distance,
|
|
211
|
+
direction: [sign(delta[0]), sign(delta[1])],
|
|
212
|
+
velocity,
|
|
213
|
+
elapsedTime: event.timeStamp - s.startTimestamp,
|
|
214
|
+
first: !s.firstFired,
|
|
215
|
+
last: true,
|
|
216
|
+
active: false,
|
|
217
|
+
tap,
|
|
218
|
+
canceled: false,
|
|
219
|
+
cancel,
|
|
220
|
+
event
|
|
221
|
+
});
|
|
222
|
+
resetDrag();
|
|
223
|
+
};
|
|
224
|
+
const onPointerCancel = (event) => {
|
|
225
|
+
const s = stateRef.current;
|
|
226
|
+
if (!s.isActive || event.pointerId !== s.pointerId) return;
|
|
227
|
+
const movement = applyAxisConstraint([event.clientX - s.startXY[0], event.clientY - s.startXY[1]]);
|
|
228
|
+
handlerRef.current({
|
|
229
|
+
xy: [event.clientX, event.clientY],
|
|
230
|
+
initial: [...s.startXY],
|
|
231
|
+
movement,
|
|
232
|
+
delta: [0, 0],
|
|
233
|
+
distance: [Math.abs(movement[0]), Math.abs(movement[1])],
|
|
234
|
+
direction: [0, 0],
|
|
235
|
+
velocity: [0, 0],
|
|
236
|
+
elapsedTime: event.timeStamp - s.startTimestamp,
|
|
237
|
+
first: !s.firstFired,
|
|
238
|
+
last: true,
|
|
239
|
+
active: false,
|
|
240
|
+
tap: false,
|
|
241
|
+
canceled: true,
|
|
242
|
+
cancel,
|
|
243
|
+
event
|
|
244
|
+
});
|
|
245
|
+
resetDrag();
|
|
246
|
+
};
|
|
247
|
+
node.addEventListener("pointerdown", onPointerDown, { signal: elementController.signal });
|
|
248
|
+
return () => {
|
|
249
|
+
elementController.abort();
|
|
250
|
+
documentControllerRef.current?.abort();
|
|
251
|
+
documentControllerRef.current = null;
|
|
252
|
+
if (stateRef.current.isActive) {
|
|
253
|
+
stateRef.current.isActive = false;
|
|
254
|
+
setActive(false);
|
|
255
|
+
document.body.style.userSelect = "";
|
|
256
|
+
document.body.style.webkitUserSelect = "";
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}, []),
|
|
260
|
+
active
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
//#endregion
|
|
264
|
+
exports.useDrag = useDrag;
|
|
265
|
+
|
|
266
|
+
//# sourceMappingURL=use-drag.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-drag.cjs","names":[],"sources":["../../src/use-drag/use-drag.ts"],"sourcesContent":["// Required to disable for webkit-user-select, although deprecated, it is still required for Safari support\n/* eslint-disable @typescript-eslint/no-deprecated */\nimport { useCallback, useRef, useState } from 'react';\n\ntype Vector2 = [number, number];\n\nexport interface UseDragState {\n /** Current pointer position [x, y] */\n xy: Vector2;\n\n /** Position where the gesture started [x, y] */\n initial: Vector2;\n\n /** Displacement from start [x, y], respects axis constraint */\n movement: Vector2;\n\n /** Change since previous event [x, y] */\n delta: Vector2;\n\n /** Absolute distance per axis [x, y] */\n distance: Vector2;\n\n /** Movement direction per axis: -1, 0, or 1 */\n direction: Vector2;\n\n /** Speed per axis in px/ms */\n velocity: Vector2;\n\n /** Time since drag started in ms */\n elapsedTime: number;\n\n /** `true` on the first handler call after the threshold is met */\n first: boolean;\n\n /** `true` on the last handler call (pointer released or canceled) */\n last: boolean;\n\n /** `true` while the gesture is ongoing */\n active: boolean;\n\n /** `true` when the gesture qualifies as a tap (requires `filterTaps: true`) */\n tap: boolean;\n\n /** `true` when the gesture was interrupted by a `pointercancel` event */\n canceled: boolean;\n\n /** Function to programmatically cancel the current gesture */\n cancel: () => void;\n\n /** The source `PointerEvent` */\n event: PointerEvent;\n}\n\nexport interface UseDragOptions {\n /** Constrain movement to a specific axis. `'lock'` locks to whichever axis has more movement after `axisThreshold` is exceeded. */\n axis?: 'x' | 'y' | 'lock';\n\n /** Movement in px required to determine axis when `axis` is `'lock'`, `1` by default */\n axisThreshold?: number;\n\n /** When `true`, the last state includes `tap: true` when total distance is below `tapThreshold`, `false` by default */\n filterTaps?: boolean;\n\n /** Max displacement in px to still be considered a tap, `3` by default */\n tapThreshold?: number;\n\n /** Minimum displacement in px before the drag activates. Can be a number (both axes) or `[x, y]`. `0` by default */\n threshold?: number | Vector2;\n\n /** Enable or disable the hook, `true` by default */\n enabled?: boolean;\n}\n\nexport interface UseDragReturnValue<T extends HTMLElement = any> {\n /** Ref callback to attach to the draggable element */\n ref: React.RefCallback<T | null>;\n\n /** `true` while the element is being dragged */\n active: boolean;\n}\n\nconst VELOCITY_DECAY_MS = 100;\n\nfunction sign(n: number): -1 | 0 | 1 {\n if (n > 0) {\n return 1;\n }\n if (n < 0) {\n return -1;\n }\n return 0;\n}\n\nfunction getThresholdVector(threshold: number | Vector2 | undefined): Vector2 {\n const t = threshold ?? 0;\n if (typeof t === 'number') {\n return [t, t];\n }\n return t;\n}\n\ninterface DragInternalState {\n isActive: boolean;\n pointerId: number;\n startXY: Vector2;\n prevXY: Vector2;\n startTimestamp: number;\n prevTimestamp: number;\n thresholdMet: boolean;\n firstFired: boolean;\n lockedAxis: 'x' | 'y' | null;\n canceled: boolean;\n lastVelocity: Vector2;\n}\n\nfunction createInitialState(): DragInternalState {\n return {\n isActive: false,\n pointerId: -1,\n startXY: [0, 0],\n prevXY: [0, 0],\n startTimestamp: 0,\n prevTimestamp: 0,\n thresholdMet: false,\n firstFired: false,\n lockedAxis: null,\n canceled: false,\n lastVelocity: [0, 0],\n };\n}\n\nexport function useDrag<T extends HTMLElement = any>(\n handler: (state: UseDragState) => void,\n options: UseDragOptions = {}\n): UseDragReturnValue<T> {\n const [active, setActive] = useState(false);\n\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const stateRef = useRef<DragInternalState>(createInitialState());\n const documentControllerRef = useRef<AbortController | null>(null);\n\n const refCallback: React.RefCallback<T | null> = useCallback((node) => {\n if (!node) {\n return undefined;\n }\n\n const elementController = new AbortController();\n\n const applyAxisConstraint = (v: Vector2): Vector2 => {\n const opts = optionsRef.current;\n const s = stateRef.current;\n\n if (opts.axis === 'x') {\n return [v[0], 0];\n }\n if (opts.axis === 'y') {\n return [0, v[1]];\n }\n if (opts.axis === 'lock') {\n if (s.lockedAxis === null) {\n const t = opts.axisThreshold ?? 1;\n if (Math.abs(v[0]) > t || Math.abs(v[1]) > t) {\n s.lockedAxis = Math.abs(v[0]) >= Math.abs(v[1]) ? 'x' : 'y';\n }\n }\n if (s.lockedAxis === 'x') {\n return [v[0], 0];\n }\n if (s.lockedAxis === 'y') {\n return [0, v[1]];\n }\n }\n return v;\n };\n\n const resetDrag = () => {\n const s = stateRef.current;\n s.isActive = false;\n s.pointerId = -1;\n s.thresholdMet = false;\n s.firstFired = false;\n s.lockedAxis = null;\n s.canceled = false;\n setActive(false);\n document.body.style.userSelect = '';\n document.body.style.webkitUserSelect = '';\n documentControllerRef.current?.abort();\n documentControllerRef.current = null;\n };\n\n const cancel = () => {\n if (stateRef.current.isActive) {\n stateRef.current.canceled = true;\n resetDrag();\n }\n };\n\n const activateDrag = () => {\n setActive(true);\n document.body.style.userSelect = 'none';\n document.body.style.webkitUserSelect = 'none';\n };\n\n const onPointerDown = (event: PointerEvent) => {\n if (optionsRef.current.enabled === false) {\n return;\n }\n if (event.button !== 0) {\n return;\n }\n if (stateRef.current.isActive) {\n return;\n }\n\n const s = stateRef.current;\n s.isActive = true;\n s.pointerId = event.pointerId;\n s.startXY = [event.clientX, event.clientY];\n s.prevXY = [event.clientX, event.clientY];\n s.startTimestamp = event.timeStamp;\n s.prevTimestamp = event.timeStamp;\n s.thresholdMet = false;\n s.firstFired = false;\n s.lockedAxis = null;\n s.canceled = false;\n s.lastVelocity = [0, 0];\n\n const [tx, ty] = getThresholdVector(optionsRef.current.threshold);\n if (tx === 0 && ty === 0) {\n s.thresholdMet = true;\n s.firstFired = true;\n activateDrag();\n\n handlerRef.current({\n xy: [event.clientX, event.clientY],\n initial: [event.clientX, event.clientY],\n movement: [0, 0],\n delta: [0, 0],\n distance: [0, 0],\n direction: [0, 0],\n velocity: [0, 0],\n elapsedTime: 0,\n first: true,\n last: false,\n active: true,\n tap: false,\n canceled: false,\n cancel,\n event,\n });\n }\n\n documentControllerRef.current?.abort();\n documentControllerRef.current = new AbortController();\n const sig = documentControllerRef.current.signal;\n\n document.addEventListener('pointermove', onPointerMove, { signal: sig });\n document.addEventListener('pointerup', onPointerUp, { signal: sig });\n document.addEventListener('pointercancel', onPointerCancel, { signal: sig });\n };\n\n const onPointerMove = (event: PointerEvent) => {\n const s = stateRef.current;\n if (!s.isActive || event.pointerId !== s.pointerId) {\n return;\n }\n\n const rawMovement: Vector2 = [event.clientX - s.startXY[0], event.clientY - s.startXY[1]];\n\n if (!s.thresholdMet) {\n const [tx, ty] = getThresholdVector(optionsRef.current.threshold);\n if (Math.abs(rawMovement[0]) < tx && Math.abs(rawMovement[1]) < ty) {\n s.prevXY = [event.clientX, event.clientY];\n s.prevTimestamp = event.timeStamp;\n return;\n }\n s.thresholdMet = true;\n activateDrag();\n }\n\n const movement = applyAxisConstraint(rawMovement);\n const rawDelta: Vector2 = [event.clientX - s.prevXY[0], event.clientY - s.prevXY[1]];\n const delta = applyAxisConstraint(rawDelta);\n const timeDelta = event.timeStamp - s.prevTimestamp;\n const velocity: Vector2 =\n timeDelta > 0\n ? [Math.abs(delta[0]) / timeDelta, Math.abs(delta[1]) / timeDelta]\n : s.lastVelocity;\n\n s.lastVelocity = velocity;\n const isFirst = !s.firstFired;\n s.firstFired = true;\n s.prevXY = [event.clientX, event.clientY];\n s.prevTimestamp = event.timeStamp;\n\n handlerRef.current({\n xy: [event.clientX, event.clientY],\n initial: [...s.startXY],\n movement,\n delta,\n distance: [Math.abs(movement[0]), Math.abs(movement[1])],\n direction: [sign(delta[0]), sign(delta[1])],\n velocity,\n elapsedTime: event.timeStamp - s.startTimestamp,\n first: isFirst,\n last: false,\n active: true,\n tap: false,\n canceled: false,\n cancel,\n event,\n });\n };\n\n const onPointerUp = (event: PointerEvent) => {\n const s = stateRef.current;\n if (!s.isActive || event.pointerId !== s.pointerId) {\n return;\n }\n\n const opts = optionsRef.current;\n\n if (!s.thresholdMet) {\n if (opts.filterTaps) {\n const rawMov: Vector2 = [event.clientX - s.startXY[0], event.clientY - s.startXY[1]];\n const mov = applyAxisConstraint(rawMov);\n const dist: Vector2 = [Math.abs(mov[0]), Math.abs(mov[1])];\n const maxDist = Math.max(dist[0], dist[1]);\n const isTap = maxDist < (opts.tapThreshold ?? 3);\n\n handlerRef.current({\n xy: [event.clientX, event.clientY],\n initial: [...s.startXY],\n movement: mov,\n delta: mov,\n distance: dist,\n direction: [sign(mov[0]), sign(mov[1])],\n velocity: [0, 0],\n elapsedTime: event.timeStamp - s.startTimestamp,\n first: true,\n last: true,\n active: false,\n tap: isTap,\n canceled: false,\n cancel,\n event,\n });\n }\n resetDrag();\n return;\n }\n\n const rawMovement: Vector2 = [event.clientX - s.startXY[0], event.clientY - s.startXY[1]];\n const movement = applyAxisConstraint(rawMovement);\n const distance: Vector2 = [Math.abs(movement[0]), Math.abs(movement[1])];\n const rawDelta: Vector2 = [event.clientX - s.prevXY[0], event.clientY - s.prevXY[1]];\n const delta = applyAxisConstraint(rawDelta);\n\n const timeSinceLastMove = event.timeStamp - s.prevTimestamp;\n const velocity: Vector2 = timeSinceLastMove > VELOCITY_DECAY_MS ? [0, 0] : s.lastVelocity;\n\n const maxDistance = Math.max(distance[0], distance[1]);\n const tap = opts.filterTaps === true && maxDistance < (opts.tapThreshold ?? 3);\n\n handlerRef.current({\n xy: [event.clientX, event.clientY],\n initial: [...s.startXY],\n movement,\n delta,\n distance,\n direction: [sign(delta[0]), sign(delta[1])],\n velocity,\n elapsedTime: event.timeStamp - s.startTimestamp,\n first: !s.firstFired,\n last: true,\n active: false,\n tap,\n canceled: false,\n cancel,\n event,\n });\n\n resetDrag();\n };\n\n const onPointerCancel = (event: PointerEvent) => {\n const s = stateRef.current;\n if (!s.isActive || event.pointerId !== s.pointerId) {\n return;\n }\n\n const rawMovement: Vector2 = [event.clientX - s.startXY[0], event.clientY - s.startXY[1]];\n const movement = applyAxisConstraint(rawMovement);\n\n handlerRef.current({\n xy: [event.clientX, event.clientY],\n initial: [...s.startXY],\n movement,\n delta: [0, 0],\n distance: [Math.abs(movement[0]), Math.abs(movement[1])],\n direction: [0, 0],\n velocity: [0, 0],\n elapsedTime: event.timeStamp - s.startTimestamp,\n first: !s.firstFired,\n last: true,\n active: false,\n tap: false,\n canceled: true,\n cancel,\n event,\n });\n\n resetDrag();\n };\n\n node.addEventListener('pointerdown', onPointerDown, {\n signal: elementController.signal,\n });\n\n return () => {\n elementController.abort();\n documentControllerRef.current?.abort();\n documentControllerRef.current = null;\n if (stateRef.current.isActive) {\n stateRef.current.isActive = false;\n setActive(false);\n document.body.style.userSelect = '';\n document.body.style.webkitUserSelect = '';\n }\n };\n }, []);\n\n return { ref: refCallback, active };\n}\n\nexport namespace useDrag {\n export type State = UseDragState;\n export type Options = UseDragOptions;\n export type ReturnValue<T extends HTMLElement = any> = UseDragReturnValue<T>;\n}\n"],"mappings":"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"}
|
|
@@ -14,7 +14,13 @@ function useFocusReturn({ opened, shouldReturnFocus = true }) {
|
|
|
14
14
|
};
|
|
15
15
|
document.addEventListener("keydown", clearFocusTimeout);
|
|
16
16
|
if (opened) lastActiveElement.current = document.activeElement;
|
|
17
|
-
else if (shouldReturnFocus)
|
|
17
|
+
else if (shouldReturnFocus) {
|
|
18
|
+
const activeElementAtClose = document.activeElement;
|
|
19
|
+
timeout = window.setTimeout(() => {
|
|
20
|
+
const currentActiveElement = document.activeElement;
|
|
21
|
+
if (currentActiveElement === null || currentActiveElement === document.body || currentActiveElement === activeElementAtClose) returnFocus();
|
|
22
|
+
}, 10);
|
|
23
|
+
}
|
|
18
24
|
return () => {
|
|
19
25
|
window.clearTimeout(timeout);
|
|
20
26
|
document.removeEventListener("keydown", clearFocusTimeout);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-focus-return.cjs","names":[],"sources":["../../src/use-focus-return/use-focus-return.ts"],"sourcesContent":["import { useRef } from 'react';\nimport { useDidUpdate } from '../use-did-update/use-did-update';\n\nexport interface UseFocusReturnInput {\n opened: boolean;\n shouldReturnFocus?: boolean;\n}\n\nexport type UseFocusReturnReturnValue = () => void;\n\nexport function useFocusReturn({\n opened,\n shouldReturnFocus = true,\n}: UseFocusReturnInput): UseFocusReturnReturnValue {\n const lastActiveElement = useRef<HTMLElement>(null);\n const returnFocus = () => {\n if (\n lastActiveElement.current &&\n 'focus' in lastActiveElement.current &&\n typeof lastActiveElement.current.focus === 'function'\n ) {\n lastActiveElement.current?.focus({ preventScroll: true });\n }\n };\n\n useDidUpdate(() => {\n let timeout = -1;\n\n const clearFocusTimeout = (event: KeyboardEvent) => {\n if (event.key === 'Tab') {\n window.clearTimeout(timeout);\n }\n };\n\n document.addEventListener('keydown', clearFocusTimeout);\n\n if (opened) {\n lastActiveElement.current = document.activeElement as HTMLElement;\n } else if (shouldReturnFocus) {\n timeout = window.setTimeout(returnFocus, 10);\n }\n\n return () => {\n window.clearTimeout(timeout);\n document.removeEventListener('keydown', clearFocusTimeout);\n };\n }, [opened, shouldReturnFocus]);\n\n return returnFocus;\n}\n\nexport namespace useFocusReturn {\n export type Input = UseFocusReturnInput;\n export type ReturnValue = UseFocusReturnReturnValue;\n}\n"],"mappings":";;;;AAUA,SAAgB,eAAe,EAC7B,QACA,oBAAoB,QAC6B;CACjD,MAAM,qBAAA,GAAA,MAAA,QAAwC,KAAK;CACnD,MAAM,oBAAoB;AACxB,MACE,kBAAkB,WAClB,WAAW,kBAAkB,WAC7B,OAAO,kBAAkB,QAAQ,UAAU,WAE3C,mBAAkB,SAAS,MAAM,EAAE,eAAe,MAAM,CAAC;;AAI7D,wBAAA,mBAAmB;EACjB,IAAI,UAAU;EAEd,MAAM,qBAAqB,UAAyB;AAClD,OAAI,MAAM,QAAQ,MAChB,QAAO,aAAa,QAAQ;;AAIhC,WAAS,iBAAiB,WAAW,kBAAkB;AAEvD,MAAI,OACF,mBAAkB,UAAU,SAAS;WAC5B,
|
|
1
|
+
{"version":3,"file":"use-focus-return.cjs","names":[],"sources":["../../src/use-focus-return/use-focus-return.ts"],"sourcesContent":["import { useRef } from 'react';\nimport { useDidUpdate } from '../use-did-update/use-did-update';\n\nexport interface UseFocusReturnInput {\n opened: boolean;\n shouldReturnFocus?: boolean;\n}\n\nexport type UseFocusReturnReturnValue = () => void;\n\nexport function useFocusReturn({\n opened,\n shouldReturnFocus = true,\n}: UseFocusReturnInput): UseFocusReturnReturnValue {\n const lastActiveElement = useRef<HTMLElement>(null);\n const returnFocus = () => {\n if (\n lastActiveElement.current &&\n 'focus' in lastActiveElement.current &&\n typeof lastActiveElement.current.focus === 'function'\n ) {\n lastActiveElement.current?.focus({ preventScroll: true });\n }\n };\n\n useDidUpdate(() => {\n let timeout = -1;\n\n const clearFocusTimeout = (event: KeyboardEvent) => {\n if (event.key === 'Tab') {\n window.clearTimeout(timeout);\n }\n };\n\n document.addEventListener('keydown', clearFocusTimeout);\n\n if (opened) {\n lastActiveElement.current = document.activeElement as HTMLElement;\n } else if (shouldReturnFocus) {\n const activeElementAtClose = document.activeElement;\n timeout = window.setTimeout(() => {\n const currentActiveElement = document.activeElement;\n if (\n currentActiveElement === null ||\n currentActiveElement === document.body ||\n currentActiveElement === activeElementAtClose\n ) {\n returnFocus();\n }\n }, 10);\n }\n\n return () => {\n window.clearTimeout(timeout);\n document.removeEventListener('keydown', clearFocusTimeout);\n };\n }, [opened, shouldReturnFocus]);\n\n return returnFocus;\n}\n\nexport namespace useFocusReturn {\n export type Input = UseFocusReturnInput;\n export type ReturnValue = UseFocusReturnReturnValue;\n}\n"],"mappings":";;;;AAUA,SAAgB,eAAe,EAC7B,QACA,oBAAoB,QAC6B;CACjD,MAAM,qBAAA,GAAA,MAAA,QAAwC,KAAK;CACnD,MAAM,oBAAoB;AACxB,MACE,kBAAkB,WAClB,WAAW,kBAAkB,WAC7B,OAAO,kBAAkB,QAAQ,UAAU,WAE3C,mBAAkB,SAAS,MAAM,EAAE,eAAe,MAAM,CAAC;;AAI7D,wBAAA,mBAAmB;EACjB,IAAI,UAAU;EAEd,MAAM,qBAAqB,UAAyB;AAClD,OAAI,MAAM,QAAQ,MAChB,QAAO,aAAa,QAAQ;;AAIhC,WAAS,iBAAiB,WAAW,kBAAkB;AAEvD,MAAI,OACF,mBAAkB,UAAU,SAAS;WAC5B,mBAAmB;GAC5B,MAAM,uBAAuB,SAAS;AACtC,aAAU,OAAO,iBAAiB;IAChC,MAAM,uBAAuB,SAAS;AACtC,QACE,yBAAyB,QACzB,yBAAyB,SAAS,QAClC,yBAAyB,qBAEzB,cAAa;MAEd,GAAG;;AAGR,eAAa;AACX,UAAO,aAAa,QAAQ;AAC5B,YAAS,oBAAoB,WAAW,kBAAkB;;IAE3D,CAAC,QAAQ,kBAAkB,CAAC;AAE/B,QAAO"}
|
|
@@ -231,12 +231,14 @@ function useMask(options) {
|
|
|
231
231
|
updateValue(reformatted, reformatted.length);
|
|
232
232
|
}, [updateValue]);
|
|
233
233
|
const clampCursorToProcessed = (0, react.useCallback)((input) => {
|
|
234
|
+
const start = input.selectionStart ?? 0;
|
|
235
|
+
if (start !== (input.selectionEnd ?? 0)) return;
|
|
234
236
|
const opts = optionsRef.current;
|
|
235
237
|
const { slots } = getResolvedOptions(opts, "");
|
|
236
238
|
const processed = processedRef.current;
|
|
237
|
-
const
|
|
238
|
-
const
|
|
239
|
-
if (
|
|
239
|
+
const endPos = processed.length > 0 ? findNextEditablePosition(processed.length, slots, processed) : findNextTokenIndex(slots, 0);
|
|
240
|
+
const startPos = findNextTokenIndex(slots, 0);
|
|
241
|
+
if (start > endPos || start < startPos) input.setSelectionRange(endPos, endPos);
|
|
240
242
|
}, []);
|
|
241
243
|
const handleFocus = (0, react.useCallback)(() => {
|
|
242
244
|
isFocusedRef.current = true;
|
|
@@ -260,13 +262,28 @@ function useMask(options) {
|
|
|
260
262
|
if (!input || input !== document.activeElement) return;
|
|
261
263
|
clampCursorToProcessed(input);
|
|
262
264
|
}, [clampCursorToProcessed]);
|
|
265
|
+
const handleMouseDown = (0, react.useCallback)(() => {
|
|
266
|
+
const input = inputRef.current;
|
|
267
|
+
if (!input) return;
|
|
268
|
+
requestAnimationFrame(() => {
|
|
269
|
+
if (input !== document.activeElement) return;
|
|
270
|
+
const start = input.selectionStart ?? 0;
|
|
271
|
+
if (start !== (input.selectionEnd ?? 0)) return;
|
|
272
|
+
const opts = optionsRef.current;
|
|
273
|
+
const { slots } = getResolvedOptions(opts, "");
|
|
274
|
+
const processed = processedRef.current;
|
|
275
|
+
const endPos = processed.length > 0 ? findNextEditablePosition(processed.length, slots, processed) : findNextTokenIndex(slots, 0);
|
|
276
|
+
if (start > endPos) input.setSelectionRange(endPos, endPos);
|
|
277
|
+
});
|
|
278
|
+
}, []);
|
|
263
279
|
const handleBlur = (0, react.useCallback)(() => {
|
|
264
280
|
isFocusedRef.current = false;
|
|
265
281
|
const opts = optionsRef.current;
|
|
266
282
|
const input = inputRef.current;
|
|
267
283
|
if (!input) return;
|
|
268
284
|
const { slots, slotChar } = getResolvedOptions(opts, rawValue);
|
|
269
|
-
const
|
|
285
|
+
const expectedFocusDisplay = buildDisplayValue(processedRef.current, slots, slotChar, true);
|
|
286
|
+
const processed = input.value === expectedFocusDisplay ? processedRef.current : processInput(input.value, slots, slotChar);
|
|
270
287
|
const complete = checkComplete(processed, slots);
|
|
271
288
|
if (opts.autoClear && !complete && processed.length > 0) {
|
|
272
289
|
input.value = "";
|
|
@@ -283,6 +300,15 @@ function useMask(options) {
|
|
|
283
300
|
return;
|
|
284
301
|
}
|
|
285
302
|
if (!opts.alwaysShowMask && !complete) {
|
|
303
|
+
if (extractRaw(processed, slots).length === 0) {
|
|
304
|
+
input.value = "";
|
|
305
|
+
processedRef.current = "";
|
|
306
|
+
setMaskedValue("");
|
|
307
|
+
setRawValue("");
|
|
308
|
+
wasCompleteRef.current = false;
|
|
309
|
+
if (opts.onChangeRaw) opts.onChangeRaw("", "");
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
286
312
|
const display = buildDisplayValue(processed, slots, slotChar, false);
|
|
287
313
|
input.value = display;
|
|
288
314
|
setMaskedValue(display);
|
|
@@ -382,6 +408,7 @@ function useMask(options) {
|
|
|
382
408
|
prevInput.removeEventListener("input", handleInput);
|
|
383
409
|
prevInput.removeEventListener("focus", handleFocus);
|
|
384
410
|
prevInput.removeEventListener("blur", handleBlur);
|
|
411
|
+
prevInput.removeEventListener("mousedown", handleMouseDown);
|
|
385
412
|
prevInput.removeEventListener("mouseup", handleMouseUp);
|
|
386
413
|
prevInput.removeEventListener("keydown", handleKeyDown);
|
|
387
414
|
prevInput.removeEventListener("paste", handlePaste);
|
|
@@ -391,6 +418,7 @@ function useMask(options) {
|
|
|
391
418
|
node.addEventListener("input", handleInput);
|
|
392
419
|
node.addEventListener("focus", handleFocus);
|
|
393
420
|
node.addEventListener("blur", handleBlur);
|
|
421
|
+
node.addEventListener("mousedown", handleMouseDown);
|
|
394
422
|
node.addEventListener("mouseup", handleMouseUp);
|
|
395
423
|
node.addEventListener("keydown", handleKeyDown);
|
|
396
424
|
node.addEventListener("paste", handlePaste);
|
|
@@ -406,6 +434,7 @@ function useMask(options) {
|
|
|
406
434
|
handleInput,
|
|
407
435
|
handleFocus,
|
|
408
436
|
handleBlur,
|
|
437
|
+
handleMouseDown,
|
|
409
438
|
handleMouseUp,
|
|
410
439
|
handleKeyDown,
|
|
411
440
|
handlePaste,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-mask.cjs","names":[],"sources":["../../src/use-mask/use-mask.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nconst DEFAULT_TOKENS: Record<string, RegExp> = {\n '9': /[0-9]/,\n a: /[A-Za-z]/,\n A: /[A-Z]/,\n '*': /[A-Za-z0-9]/,\n '#': /[-+0-9]/,\n};\n\nexport interface UseMaskOptions {\n /** Mask pattern string or array of string literals and RegExp objects */\n mask: string | Array<string | RegExp>;\n\n /** Override or extend the default token map */\n tokens?: Record<string, RegExp>;\n\n /** Called before masking on each keystroke, can return overrides for mask options */\n modify?: (\n value: string\n ) => Partial<Pick<UseMaskOptions, 'mask' | 'tokens' | 'slotChar' | 'separate'>> | undefined;\n\n /** When true, raw and display values are decoupled */\n separate?: boolean;\n\n /** Character displayed in unfilled slots, `\"_\"` by default */\n slotChar?: string | null;\n\n /** Show mask pattern even when field is empty and unfocused */\n alwaysShowMask?: boolean;\n\n /** Show mask placeholder on focus, `true` by default */\n showMaskOnFocus?: boolean;\n\n /** Transform each character before validation and insertion */\n transform?: (char: string) => string;\n\n /** Clear value on blur when mask is incomplete, `false` by default */\n autoClear?: boolean;\n\n /** Sets aria-invalid on the input */\n invalid?: boolean;\n\n /** Called on every change with raw and masked values */\n onChangeRaw?: (rawValue: string, maskedValue: string) => void;\n\n /** Called when all required mask slots are filled */\n onComplete?: (maskedValue: string, rawValue: string) => void;\n\n /** Escape hatch for advanced cursor/value manipulation */\n beforeMaskedStateChange?: (states: {\n previousState: MaskState;\n currentState: MaskState;\n nextState: MaskState;\n }) => MaskState;\n}\n\nexport interface MaskState {\n value: string;\n selection: { start: number; end: number } | null;\n}\n\nexport interface UseMaskReturnValue {\n /** Ref to attach to the input element */\n ref: React.RefCallback<HTMLInputElement>;\n\n /** Current masked display value */\n value: string;\n\n /** Current raw unmasked value */\n rawValue: string;\n\n /** Whether all required mask slots are filled */\n isComplete: boolean;\n\n /** Clear the input value and reset state */\n reset: () => void;\n}\n\ninterface MaskSlot {\n type: 'token' | 'literal';\n char: string;\n pattern?: RegExp;\n optional?: boolean;\n}\n\nfunction parseMask(\n mask: string | Array<string | RegExp>,\n tokens: Record<string, RegExp>\n): MaskSlot[] {\n if (Array.isArray(mask)) {\n return mask.map((item) => {\n if (item instanceof RegExp) {\n return { type: 'token', char: '_', pattern: item };\n }\n return { type: 'literal', char: item };\n });\n }\n\n const slots: MaskSlot[] = [];\n let optional = false;\n\n for (let i = 0; i < mask.length; i++) {\n const char = mask[i];\n\n if (char === '\\\\' && i + 1 < mask.length) {\n i++;\n slots.push({ type: 'literal', char: mask[i] });\n continue;\n }\n\n if (char === '?') {\n optional = true;\n continue;\n }\n\n if (tokens[char]) {\n slots.push({ type: 'token', char, pattern: tokens[char], optional });\n } else {\n slots.push({ type: 'literal', char, optional });\n }\n }\n\n return slots;\n}\n\nfunction getSlotChar(slotCharOption: string | null | undefined, index: number): string {\n if (slotCharOption === null || slotCharOption === '' || slotCharOption === undefined) {\n return '';\n }\n if (slotCharOption.length > 1) {\n return slotCharOption[index] || '_';\n }\n return slotCharOption;\n}\n\nfunction applyMaskToRaw(\n raw: string,\n slots: MaskSlot[],\n _slotCharOption: string | null | undefined,\n transform?: (char: string) => string\n): string {\n let result = '';\n let rawIndex = 0;\n let slotIndex = 0;\n\n for (slotIndex = 0; slotIndex < slots.length; slotIndex++) {\n const slot = slots[slotIndex];\n if (slot.type === 'literal') {\n result += slot.char;\n } else if (rawIndex < raw.length) {\n const ch = transform ? transform(raw[rawIndex]) : raw[rawIndex];\n if (slot.pattern && slot.pattern.test(ch)) {\n result += ch;\n rawIndex++;\n } else {\n rawIndex++;\n slotIndex--;\n }\n } else {\n break;\n }\n }\n\n return result;\n}\n\nfunction buildDisplayValue(\n value: string,\n slots: MaskSlot[],\n slotCharOption: string | null | undefined,\n showSlots: boolean\n): string {\n if (!showSlots) {\n return value;\n }\n\n let display = value;\n\n for (let i = value.length; i < slots.length; i++) {\n const slot = slots[i];\n if (slot.type === 'literal') {\n display += slot.char;\n } else {\n const sc = getSlotChar(slotCharOption, i);\n if (!sc) {\n break;\n }\n display += sc;\n }\n }\n\n return display;\n}\n\nfunction extractRaw(masked: string, slots: MaskSlot[]): string {\n let raw = '';\n for (let i = 0; i < masked.length && i < slots.length; i++) {\n if (slots[i].type === 'token') {\n raw += masked[i];\n }\n }\n return raw;\n}\n\nfunction checkComplete(masked: string, slots: MaskSlot[]): boolean {\n for (let i = 0; i < slots.length; i++) {\n if (slots[i].type === 'token' && !slots[i].optional) {\n if (i >= masked.length) {\n return false;\n }\n if (!slots[i].pattern!.test(masked[i])) {\n return false;\n }\n }\n }\n return true;\n}\n\nfunction findNextTokenIndex(slots: MaskSlot[], from: number): number {\n for (let i = from; i < slots.length; i++) {\n if (slots[i].type === 'token') {\n return i;\n }\n }\n return slots.length;\n}\n\nfunction findPrevTokenIndex(slots: MaskSlot[], from: number): number {\n for (let i = from; i >= 0; i--) {\n if (slots[i].type === 'token') {\n return i;\n }\n }\n return -1;\n}\n\nfunction processInput(\n inputValue: string,\n slots: MaskSlot[],\n _slotCharOption: string | null | undefined\n): string {\n let result = '';\n let inputIndex = 0;\n\n for (\n let slotIndex = 0;\n slotIndex < slots.length && inputIndex <= inputValue.length;\n slotIndex++\n ) {\n const slot = slots[slotIndex];\n\n if (slot.type === 'literal') {\n result += slot.char;\n if (inputIndex < inputValue.length && inputValue[inputIndex] === slot.char) {\n inputIndex++;\n }\n continue;\n }\n\n if (inputIndex >= inputValue.length) {\n break;\n }\n\n while (inputIndex < inputValue.length) {\n const ch = inputValue[inputIndex];\n inputIndex++;\n\n if (slot.pattern!.test(ch)) {\n result += ch;\n break;\n }\n }\n\n if (result.length <= slotIndex) {\n break;\n }\n }\n\n return result;\n}\n\nfunction getResolvedOptions(options: UseMaskOptions, rawValue: string) {\n const tokens = { ...DEFAULT_TOKENS, ...options.tokens };\n let mask = options.mask;\n let slotChar: string | null | undefined = options.slotChar === undefined ? '_' : options.slotChar;\n let separate = options.separate ?? false;\n\n if (options.modify) {\n const overrides = options.modify(rawValue);\n if (overrides) {\n if (overrides.mask !== undefined) {\n mask = overrides.mask;\n }\n if (overrides.tokens !== undefined) {\n Object.assign(tokens, overrides.tokens);\n }\n if (overrides.slotChar !== undefined) {\n slotChar = overrides.slotChar;\n }\n if (overrides.separate !== undefined) {\n separate = overrides.separate;\n }\n }\n }\n\n const slots = parseMask(mask, tokens);\n return { slots, slotChar, separate, tokens, transform: options.transform };\n}\n\nexport function formatMask(raw: string, options: UseMaskOptions): string {\n const { slots, slotChar, transform } = getResolvedOptions(options, raw);\n return applyMaskToRaw(raw, slots, slotChar, transform);\n}\n\nexport function unformatMask(masked: string, options: UseMaskOptions): string {\n const { slots } = getResolvedOptions(options, '');\n return extractRaw(masked, slots);\n}\n\nexport function isMaskComplete(masked: string, options: UseMaskOptions): boolean {\n const { slots } = getResolvedOptions(options, '');\n return checkComplete(masked, slots);\n}\n\nexport function generatePattern(mode: 'full' | 'full-inexact', options: UseMaskOptions): string {\n const { slots } = getResolvedOptions(options, '');\n let pattern = '';\n\n for (const slot of slots) {\n if (slot.type === 'literal') {\n pattern += slot.char.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n } else {\n const src = slot.pattern!.source;\n if (mode === 'full-inexact') {\n pattern += slot.optional ? `${src}?` : src;\n } else {\n pattern += slot.optional ? `(${src})?` : `(${src})`;\n }\n }\n }\n\n return pattern;\n}\n\nexport function useMask(options: UseMaskOptions): UseMaskReturnValue {\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const inputRef = useRef<HTMLInputElement | null>(null);\n const [maskedValue, setMaskedValue] = useState('');\n const [rawValue, setRawValue] = useState('');\n const processedRef = useRef('');\n const wasCompleteRef = useRef(false);\n const isFocusedRef = useRef(false);\n\n const getOptions = useCallback(() => {\n const opts = optionsRef.current;\n return getResolvedOptions(opts, rawValue);\n }, [rawValue]);\n\n const updateValue = useCallback(\n (newMasked: string, cursorPos?: number) => {\n const opts = optionsRef.current;\n const { slots } = getResolvedOptions(\n opts,\n extractRaw(newMasked, getResolvedOptions(opts, '').slots)\n );\n const raw = extractRaw(newMasked, slots);\n\n const { slots: resolvedSlots, slotChar } = getResolvedOptions(opts, raw);\n\n const reprocessed = processInput(newMasked, resolvedSlots, slotChar);\n const newRaw = extractRaw(reprocessed, resolvedSlots);\n\n const showSlots = opts.alwaysShowMask || isFocusedRef.current;\n const showOnFocus = opts.showMaskOnFocus !== false;\n const shouldShowSlots = showSlots && (showOnFocus || reprocessed.length > 0);\n\n const displayValue = buildDisplayValue(reprocessed, resolvedSlots, slotChar, shouldShowSlots);\n\n processedRef.current = reprocessed;\n setMaskedValue(displayValue);\n setRawValue(newRaw);\n\n if (inputRef.current) {\n inputRef.current.value = displayValue;\n if (cursorPos !== undefined && document.activeElement === inputRef.current) {\n const pos = Math.min(cursorPos, reprocessed.length);\n inputRef.current.setSelectionRange(pos, pos);\n }\n }\n\n if (opts.onChangeRaw) {\n opts.onChangeRaw(newRaw, displayValue);\n }\n\n const complete = checkComplete(reprocessed, resolvedSlots);\n if (complete && !wasCompleteRef.current && opts.onComplete) {\n opts.onComplete(displayValue, newRaw);\n }\n wasCompleteRef.current = complete;\n\n return { displayValue, newRaw, reprocessed, resolvedSlots };\n },\n [getOptions]\n );\n\n const handleInput = useCallback(\n (e: Event) => {\n const input = e.target as HTMLInputElement;\n const opts = optionsRef.current;\n\n const { slots: resolvedSlots, slotChar, transform } = getResolvedOptions(opts, '');\n const raw = extractRaw(input.value, resolvedSlots);\n const reformatted = applyMaskToRaw(raw, resolvedSlots, slotChar, transform);\n updateValue(reformatted, reformatted.length);\n },\n [updateValue]\n );\n\n const clampCursorToProcessed = useCallback((input: HTMLInputElement) => {\n const opts = optionsRef.current;\n const { slots } = getResolvedOptions(opts, '');\n const processed = processedRef.current;\n const cursorPos = input.selectionStart ?? 0;\n const maxPos =\n processed.length > 0\n ? findNextEditablePosition(processed.length, slots, processed)\n : findNextTokenIndex(slots, 0);\n\n if (cursorPos > maxPos) {\n input.setSelectionRange(maxPos, maxPos);\n }\n }, []);\n\n const handleFocus = useCallback(() => {\n isFocusedRef.current = true;\n const opts = optionsRef.current;\n const input = inputRef.current;\n\n if (!input) {\n return;\n }\n\n const { slots, slotChar } = getResolvedOptions(opts, '');\n const showOnFocus = opts.showMaskOnFocus !== false;\n const processed = processedRef.current;\n\n if (showOnFocus || opts.alwaysShowMask) {\n const display = buildDisplayValue(processed, slots, slotChar, true);\n input.value = display;\n setMaskedValue(display);\n }\n\n requestAnimationFrame(() => {\n if (input === document.activeElement) {\n clampCursorToProcessed(input);\n }\n });\n }, [clampCursorToProcessed]);\n\n const handleMouseUp = useCallback(() => {\n const input = inputRef.current;\n if (!input || input !== document.activeElement) {\n return;\n }\n\n clampCursorToProcessed(input);\n }, [clampCursorToProcessed]);\n\n const handleBlur = useCallback(() => {\n isFocusedRef.current = false;\n const opts = optionsRef.current;\n const input = inputRef.current;\n\n if (!input) {\n return;\n }\n\n const { slots, slotChar } = getResolvedOptions(opts, rawValue);\n const processed = processInput(input.value, slots, slotChar);\n const complete = checkComplete(processed, slots);\n\n if (opts.autoClear && !complete && processed.length > 0) {\n input.value = '';\n processedRef.current = '';\n setMaskedValue('');\n setRawValue('');\n wasCompleteRef.current = false;\n\n if (opts.onChangeRaw) {\n opts.onChangeRaw('', '');\n }\n\n if (opts.alwaysShowMask) {\n const emptyDisplay = buildDisplayValue('', slots, slotChar, true);\n input.value = emptyDisplay;\n setMaskedValue(emptyDisplay);\n }\n return;\n }\n\n if (!opts.alwaysShowMask && !complete) {\n const display = buildDisplayValue(processed, slots, slotChar, false);\n input.value = display;\n setMaskedValue(display);\n }\n }, [rawValue]);\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n const input = e.target as HTMLInputElement;\n const opts = optionsRef.current;\n\n const { slots, slotChar, transform } = getResolvedOptions(opts, rawValue);\n const start = input.selectionStart ?? 0;\n const end = input.selectionEnd ?? 0;\n const processed = processedRef.current;\n\n if (e.key === 'Backspace') {\n e.preventDefault();\n\n if (e.metaKey || (e.ctrlKey && !e.altKey)) {\n const clampedStart = Math.min(start, processed.length);\n const afterRaw = extractRaw(processed.slice(clampedStart), slots.slice(clampedStart));\n const newValue = applyMaskToRaw(afterRaw, slots, slotChar, transform);\n updateValue(newValue, 0);\n return;\n }\n\n if (start !== end) {\n const clampedEnd = Math.min(end, processed.length);\n const before = processed.slice(0, start);\n const afterRaw = extractRaw(processed.slice(clampedEnd), slots.slice(clampedEnd));\n const newValue = applyMaskToRaw(\n extractRaw(before, slots) + afterRaw,\n slots,\n slotChar,\n transform\n );\n updateValue(newValue, start);\n return;\n }\n\n if (start === 0) {\n return;\n }\n\n let deletePos = start - 1;\n while (deletePos >= 0 && slots[deletePos] && slots[deletePos].type === 'literal') {\n deletePos--;\n }\n\n if (deletePos < 0) {\n return;\n }\n\n const beforeRaw = extractRaw(processed.slice(0, deletePos), slots.slice(0, deletePos));\n const afterRaw = extractRaw(processed.slice(deletePos + 1), slots.slice(deletePos + 1));\n const newValue = applyMaskToRaw(beforeRaw + afterRaw, slots, slotChar, transform);\n updateValue(newValue, deletePos);\n } else if (e.key === 'Delete') {\n e.preventDefault();\n\n if (start !== end) {\n const clampedEnd = Math.min(end, processed.length);\n const before = processed.slice(0, start);\n const afterRaw = extractRaw(processed.slice(clampedEnd), slots.slice(clampedEnd));\n const newValue = applyMaskToRaw(\n extractRaw(before, slots) + afterRaw,\n slots,\n slotChar,\n transform\n );\n updateValue(newValue, start);\n return;\n }\n\n let deletePos = start;\n while (\n deletePos < slots.length &&\n slots[deletePos] &&\n slots[deletePos].type === 'literal'\n ) {\n deletePos++;\n }\n\n if (deletePos >= processed.length) {\n return;\n }\n\n const beforeRaw = extractRaw(processed.slice(0, start), slots.slice(0, start));\n const afterRaw = extractRaw(processed.slice(deletePos + 1), slots.slice(deletePos + 1));\n const newValue = applyMaskToRaw(beforeRaw + afterRaw, slots, slotChar, transform);\n updateValue(newValue, start);\n } else if (e.key === 'ArrowRight' && !e.shiftKey) {\n const nextPos = findNextEditablePosition(start + 1, slots, input.value);\n if (nextPos !== start + 1) {\n e.preventDefault();\n input.setSelectionRange(nextPos, nextPos);\n }\n } else if (e.key === 'ArrowLeft' && !e.shiftKey) {\n if (start > 0) {\n const prevToken = findPrevTokenIndex(slots, start - 1);\n if (prevToken >= 0 && prevToken !== start - 1) {\n e.preventDefault();\n input.setSelectionRange(prevToken + 1, prevToken + 1);\n }\n }\n } else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {\n e.preventDefault();\n\n let insertPos = Math.min(start, processed.length);\n while (\n insertPos < slots.length &&\n slots[insertPos] &&\n slots[insertPos].type === 'literal'\n ) {\n insertPos++;\n }\n\n if (insertPos >= slots.length) {\n return;\n }\n\n const slot = slots[insertPos];\n const ch = transform ? transform(e.key) : e.key;\n if (!slot.pattern!.test(ch)) {\n return;\n }\n\n const beforeRaw = extractRaw(processed.slice(0, insertPos), slots.slice(0, insertPos));\n const afterRaw =\n start < end\n ? extractRaw(\n processed.slice(Math.min(end, processed.length)),\n slots.slice(Math.min(end, processed.length))\n )\n : extractRaw(processed.slice(insertPos), slots.slice(insertPos));\n const newValue = applyMaskToRaw(beforeRaw + ch + afterRaw, slots, slotChar, transform);\n const newCursorPos = findNextEditablePosition(insertPos + 1, slots, newValue);\n updateValue(newValue, newCursorPos);\n }\n },\n [rawValue, updateValue]\n );\n\n const handlePaste = useCallback(\n (e: ClipboardEvent) => {\n e.preventDefault();\n const input = e.target as HTMLInputElement;\n const opts = optionsRef.current;\n\n const pastedText = e.clipboardData?.getData('text') ?? '';\n const start = input.selectionStart ?? 0;\n const end = input.selectionEnd ?? 0;\n const processed = processedRef.current;\n\n const { slots, slotChar, transform } = getResolvedOptions(opts, '');\n const clampedEnd = Math.min(end, processed.length);\n const beforeRaw = extractRaw(processed.slice(0, start), slots.slice(0, start));\n const afterRaw = extractRaw(processed.slice(clampedEnd), slots.slice(clampedEnd));\n const newValue = applyMaskToRaw(\n beforeRaw + pastedText + afterRaw,\n slots,\n slotChar,\n transform\n );\n\n const { reprocessed } = updateValue(newValue);\n\n const pasteEndPos = Math.min((reprocessed || newValue).length, slots.length);\n if (input === document.activeElement) {\n input.setSelectionRange(pasteEndPos, pasteEndPos);\n }\n },\n [updateValue]\n );\n\n const setAriaAttributes = useCallback((input: HTMLInputElement) => {\n const opts = optionsRef.current;\n\n if (opts.invalid) {\n input.setAttribute('aria-invalid', 'true');\n } else {\n input.removeAttribute('aria-invalid');\n }\n }, []);\n\n const refCallback = useCallback(\n (node: HTMLInputElement | null) => {\n const prevInput = inputRef.current;\n\n if (prevInput) {\n prevInput.removeEventListener('input', handleInput);\n prevInput.removeEventListener('focus', handleFocus);\n prevInput.removeEventListener('blur', handleBlur);\n prevInput.removeEventListener('mouseup', handleMouseUp);\n prevInput.removeEventListener('keydown', handleKeyDown as EventListener);\n prevInput.removeEventListener('paste', handlePaste as EventListener);\n }\n\n inputRef.current = node;\n\n if (node) {\n node.addEventListener('input', handleInput);\n node.addEventListener('focus', handleFocus);\n node.addEventListener('blur', handleBlur);\n node.addEventListener('mouseup', handleMouseUp);\n node.addEventListener('keydown', handleKeyDown as EventListener);\n node.addEventListener('paste', handlePaste as EventListener);\n\n setAriaAttributes(node);\n\n if (options.alwaysShowMask && !node.value) {\n const { slots, slotChar } = getResolvedOptions(options, '');\n const display = buildDisplayValue('', slots, slotChar, true);\n node.value = display;\n setMaskedValue(display);\n }\n }\n },\n [\n handleInput,\n handleFocus,\n handleBlur,\n handleMouseUp,\n handleKeyDown,\n handlePaste,\n setAriaAttributes,\n options,\n ]\n );\n\n useEffect(() => {\n const input = inputRef.current;\n if (!input) {\n return;\n }\n\n setAriaAttributes(input);\n }, [options.invalid, setAriaAttributes]);\n\n const isComplete = (() => {\n const { slots } = getOptions();\n return checkComplete(processedRef.current, slots);\n })();\n\n const reset = useCallback(() => {\n const opts = optionsRef.current;\n const input = inputRef.current;\n\n processedRef.current = '';\n setMaskedValue('');\n setRawValue('');\n wasCompleteRef.current = false;\n\n if (input) {\n if (opts.alwaysShowMask) {\n const { slots, slotChar } = getResolvedOptions(opts, '');\n const display = buildDisplayValue('', slots, slotChar, true);\n input.value = display;\n setMaskedValue(display);\n } else {\n input.value = '';\n }\n }\n\n if (opts.onChangeRaw) {\n opts.onChangeRaw('', '');\n }\n }, []);\n\n return {\n ref: refCallback,\n value: maskedValue,\n rawValue,\n isComplete,\n reset,\n };\n}\n\nfunction findNextEditablePosition(from: number, slots: MaskSlot[], value: string): number {\n let pos = from;\n while (pos < slots.length && pos < value.length && slots[pos] && slots[pos].type === 'literal') {\n pos++;\n }\n return pos;\n}\n\nexport namespace useMask {\n export type Options = UseMaskOptions;\n export type ReturnValue = UseMaskReturnValue;\n}\n"],"mappings":";;;AAEA,MAAM,iBAAyC;CAC7C,KAAK;CACL,GAAG;CACH,GAAG;CACH,KAAK;CACL,KAAK;CACN;AA8ED,SAAS,UACP,MACA,QACY;AACZ,KAAI,MAAM,QAAQ,KAAK,CACrB,QAAO,KAAK,KAAK,SAAS;AACxB,MAAI,gBAAgB,OAClB,QAAO;GAAE,MAAM;GAAS,MAAM;GAAK,SAAS;GAAM;AAEpD,SAAO;GAAE,MAAM;GAAW,MAAM;GAAM;GACtC;CAGJ,MAAM,QAAoB,EAAE;CAC5B,IAAI,WAAW;AAEf,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,OAAO,KAAK;AAElB,MAAI,SAAS,QAAQ,IAAI,IAAI,KAAK,QAAQ;AACxC;AACA,SAAM,KAAK;IAAE,MAAM;IAAW,MAAM,KAAK;IAAI,CAAC;AAC9C;;AAGF,MAAI,SAAS,KAAK;AAChB,cAAW;AACX;;AAGF,MAAI,OAAO,MACT,OAAM,KAAK;GAAE,MAAM;GAAS;GAAM,SAAS,OAAO;GAAO;GAAU,CAAC;MAEpE,OAAM,KAAK;GAAE,MAAM;GAAW;GAAM;GAAU,CAAC;;AAInD,QAAO;;AAGT,SAAS,YAAY,gBAA2C,OAAuB;AACrF,KAAI,mBAAmB,QAAQ,mBAAmB,MAAM,mBAAmB,KAAA,EACzE,QAAO;AAET,KAAI,eAAe,SAAS,EAC1B,QAAO,eAAe,UAAU;AAElC,QAAO;;AAGT,SAAS,eACP,KACA,OACA,iBACA,WACQ;CACR,IAAI,SAAS;CACb,IAAI,WAAW;CACf,IAAI,YAAY;AAEhB,MAAK,YAAY,GAAG,YAAY,MAAM,QAAQ,aAAa;EACzD,MAAM,OAAO,MAAM;AACnB,MAAI,KAAK,SAAS,UAChB,WAAU,KAAK;WACN,WAAW,IAAI,QAAQ;GAChC,MAAM,KAAK,YAAY,UAAU,IAAI,UAAU,GAAG,IAAI;AACtD,OAAI,KAAK,WAAW,KAAK,QAAQ,KAAK,GAAG,EAAE;AACzC,cAAU;AACV;UACK;AACL;AACA;;QAGF;;AAIJ,QAAO;;AAGT,SAAS,kBACP,OACA,OACA,gBACA,WACQ;AACR,KAAI,CAAC,UACH,QAAO;CAGT,IAAI,UAAU;AAEd,MAAK,IAAI,IAAI,MAAM,QAAQ,IAAI,MAAM,QAAQ,KAAK;EAChD,MAAM,OAAO,MAAM;AACnB,MAAI,KAAK,SAAS,UAChB,YAAW,KAAK;OACX;GACL,MAAM,KAAK,YAAY,gBAAgB,EAAE;AACzC,OAAI,CAAC,GACH;AAEF,cAAW;;;AAIf,QAAO;;AAGT,SAAS,WAAW,QAAgB,OAA2B;CAC7D,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,UAAU,IAAI,MAAM,QAAQ,IACrD,KAAI,MAAM,GAAG,SAAS,QACpB,QAAO,OAAO;AAGlB,QAAO;;AAGT,SAAS,cAAc,QAAgB,OAA4B;AACjE,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,IAChC,KAAI,MAAM,GAAG,SAAS,WAAW,CAAC,MAAM,GAAG,UAAU;AACnD,MAAI,KAAK,OAAO,OACd,QAAO;AAET,MAAI,CAAC,MAAM,GAAG,QAAS,KAAK,OAAO,GAAG,CACpC,QAAO;;AAIb,QAAO;;AAGT,SAAS,mBAAmB,OAAmB,MAAsB;AACnE,MAAK,IAAI,IAAI,MAAM,IAAI,MAAM,QAAQ,IACnC,KAAI,MAAM,GAAG,SAAS,QACpB,QAAO;AAGX,QAAO,MAAM;;AAGf,SAAS,mBAAmB,OAAmB,MAAsB;AACnE,MAAK,IAAI,IAAI,MAAM,KAAK,GAAG,IACzB,KAAI,MAAM,GAAG,SAAS,QACpB,QAAO;AAGX,QAAO;;AAGT,SAAS,aACP,YACA,OACA,iBACQ;CACR,IAAI,SAAS;CACb,IAAI,aAAa;AAEjB,MACE,IAAI,YAAY,GAChB,YAAY,MAAM,UAAU,cAAc,WAAW,QACrD,aACA;EACA,MAAM,OAAO,MAAM;AAEnB,MAAI,KAAK,SAAS,WAAW;AAC3B,aAAU,KAAK;AACf,OAAI,aAAa,WAAW,UAAU,WAAW,gBAAgB,KAAK,KACpE;AAEF;;AAGF,MAAI,cAAc,WAAW,OAC3B;AAGF,SAAO,aAAa,WAAW,QAAQ;GACrC,MAAM,KAAK,WAAW;AACtB;AAEA,OAAI,KAAK,QAAS,KAAK,GAAG,EAAE;AAC1B,cAAU;AACV;;;AAIJ,MAAI,OAAO,UAAU,UACnB;;AAIJ,QAAO;;AAGT,SAAS,mBAAmB,SAAyB,UAAkB;CACrE,MAAM,SAAS;EAAE,GAAG;EAAgB,GAAG,QAAQ;EAAQ;CACvD,IAAI,OAAO,QAAQ;CACnB,IAAI,WAAsC,QAAQ,aAAa,KAAA,IAAY,MAAM,QAAQ;CACzF,IAAI,WAAW,QAAQ,YAAY;AAEnC,KAAI,QAAQ,QAAQ;EAClB,MAAM,YAAY,QAAQ,OAAO,SAAS;AAC1C,MAAI,WAAW;AACb,OAAI,UAAU,SAAS,KAAA,EACrB,QAAO,UAAU;AAEnB,OAAI,UAAU,WAAW,KAAA,EACvB,QAAO,OAAO,QAAQ,UAAU,OAAO;AAEzC,OAAI,UAAU,aAAa,KAAA,EACzB,YAAW,UAAU;AAEvB,OAAI,UAAU,aAAa,KAAA,EACzB,YAAW,UAAU;;;AAM3B,QAAO;EAAE,OADK,UAAU,MAAM,OAAO;EACrB;EAAU;EAAU;EAAQ,WAAW,QAAQ;EAAW;;AAG5E,SAAgB,WAAW,KAAa,SAAiC;CACvE,MAAM,EAAE,OAAO,UAAU,cAAc,mBAAmB,SAAS,IAAI;AACvE,QAAO,eAAe,KAAK,OAAO,UAAU,UAAU;;AAGxD,SAAgB,aAAa,QAAgB,SAAiC;CAC5E,MAAM,EAAE,UAAU,mBAAmB,SAAS,GAAG;AACjD,QAAO,WAAW,QAAQ,MAAM;;AAGlC,SAAgB,eAAe,QAAgB,SAAkC;CAC/E,MAAM,EAAE,UAAU,mBAAmB,SAAS,GAAG;AACjD,QAAO,cAAc,QAAQ,MAAM;;AAGrC,SAAgB,gBAAgB,MAA+B,SAAiC;CAC9F,MAAM,EAAE,UAAU,mBAAmB,SAAS,GAAG;CACjD,IAAI,UAAU;AAEd,MAAK,MAAM,QAAQ,MACjB,KAAI,KAAK,SAAS,UAChB,YAAW,KAAK,KAAK,QAAQ,uBAAuB,OAAO;MACtD;EACL,MAAM,MAAM,KAAK,QAAS;AAC1B,MAAI,SAAS,eACX,YAAW,KAAK,WAAW,GAAG,IAAI,KAAK;MAEvC,YAAW,KAAK,WAAW,IAAI,IAAI,MAAM,IAAI,IAAI;;AAKvD,QAAO;;AAGT,SAAgB,QAAQ,SAA6C;CACnE,MAAM,cAAA,GAAA,MAAA,QAAoB,QAAQ;AAClC,YAAW,UAAU;CAErB,MAAM,YAAA,GAAA,MAAA,QAA2C,KAAK;CACtD,MAAM,CAAC,aAAa,mBAAA,GAAA,MAAA,UAA2B,GAAG;CAClD,MAAM,CAAC,UAAU,gBAAA,GAAA,MAAA,UAAwB,GAAG;CAC5C,MAAM,gBAAA,GAAA,MAAA,QAAsB,GAAG;CAC/B,MAAM,kBAAA,GAAA,MAAA,QAAwB,MAAM;CACpC,MAAM,gBAAA,GAAA,MAAA,QAAsB,MAAM;CAElC,MAAM,cAAA,GAAA,MAAA,mBAA+B;EACnC,MAAM,OAAO,WAAW;AACxB,SAAO,mBAAmB,MAAM,SAAS;IACxC,CAAC,SAAS,CAAC;CAEd,MAAM,eAAA,GAAA,MAAA,cACH,WAAmB,cAAuB;EACzC,MAAM,OAAO,WAAW;EACxB,MAAM,EAAE,UAAU,mBAChB,MACA,WAAW,WAAW,mBAAmB,MAAM,GAAG,CAAC,MAAM,CAC1D;EAGD,MAAM,EAAE,OAAO,eAAe,aAAa,mBAAmB,MAFlD,WAAW,WAAW,MAAM,CAEgC;EAExE,MAAM,cAAc,aAAa,WAAW,eAAe,SAAS;EACpE,MAAM,SAAS,WAAW,aAAa,cAAc;EAErD,MAAM,YAAY,KAAK,kBAAkB,aAAa;EACtD,MAAM,cAAc,KAAK,oBAAoB;EAG7C,MAAM,eAAe,kBAAkB,aAAa,eAAe,UAF3C,cAAc,eAAe,YAAY,SAAS,GAEmB;AAE7F,eAAa,UAAU;AACvB,iBAAe,aAAa;AAC5B,cAAY,OAAO;AAEnB,MAAI,SAAS,SAAS;AACpB,YAAS,QAAQ,QAAQ;AACzB,OAAI,cAAc,KAAA,KAAa,SAAS,kBAAkB,SAAS,SAAS;IAC1E,MAAM,MAAM,KAAK,IAAI,WAAW,YAAY,OAAO;AACnD,aAAS,QAAQ,kBAAkB,KAAK,IAAI;;;AAIhD,MAAI,KAAK,YACP,MAAK,YAAY,QAAQ,aAAa;EAGxC,MAAM,WAAW,cAAc,aAAa,cAAc;AAC1D,MAAI,YAAY,CAAC,eAAe,WAAW,KAAK,WAC9C,MAAK,WAAW,cAAc,OAAO;AAEvC,iBAAe,UAAU;AAEzB,SAAO;GAAE;GAAc;GAAQ;GAAa;GAAe;IAE7D,CAAC,WAAW,CACb;CAED,MAAM,eAAA,GAAA,MAAA,cACH,MAAa;EACZ,MAAM,QAAQ,EAAE;EAChB,MAAM,OAAO,WAAW;EAExB,MAAM,EAAE,OAAO,eAAe,UAAU,cAAc,mBAAmB,MAAM,GAAG;EAElF,MAAM,cAAc,eADR,WAAW,MAAM,OAAO,cAAc,EACV,eAAe,UAAU,UAAU;AAC3E,cAAY,aAAa,YAAY,OAAO;IAE9C,CAAC,YAAY,CACd;CAED,MAAM,0BAAA,GAAA,MAAA,cAAsC,UAA4B;EACtE,MAAM,OAAO,WAAW;EACxB,MAAM,EAAE,UAAU,mBAAmB,MAAM,GAAG;EAC9C,MAAM,YAAY,aAAa;EAC/B,MAAM,YAAY,MAAM,kBAAkB;EAC1C,MAAM,SACJ,UAAU,SAAS,IACf,yBAAyB,UAAU,QAAQ,OAAO,UAAU,GAC5D,mBAAmB,OAAO,EAAE;AAElC,MAAI,YAAY,OACd,OAAM,kBAAkB,QAAQ,OAAO;IAExC,EAAE,CAAC;CAEN,MAAM,eAAA,GAAA,MAAA,mBAAgC;AACpC,eAAa,UAAU;EACvB,MAAM,OAAO,WAAW;EACxB,MAAM,QAAQ,SAAS;AAEvB,MAAI,CAAC,MACH;EAGF,MAAM,EAAE,OAAO,aAAa,mBAAmB,MAAM,GAAG;EACxD,MAAM,cAAc,KAAK,oBAAoB;EAC7C,MAAM,YAAY,aAAa;AAE/B,MAAI,eAAe,KAAK,gBAAgB;GACtC,MAAM,UAAU,kBAAkB,WAAW,OAAO,UAAU,KAAK;AACnE,SAAM,QAAQ;AACd,kBAAe,QAAQ;;AAGzB,8BAA4B;AAC1B,OAAI,UAAU,SAAS,cACrB,wBAAuB,MAAM;IAE/B;IACD,CAAC,uBAAuB,CAAC;CAE5B,MAAM,iBAAA,GAAA,MAAA,mBAAkC;EACtC,MAAM,QAAQ,SAAS;AACvB,MAAI,CAAC,SAAS,UAAU,SAAS,cAC/B;AAGF,yBAAuB,MAAM;IAC5B,CAAC,uBAAuB,CAAC;CAE5B,MAAM,cAAA,GAAA,MAAA,mBAA+B;AACnC,eAAa,UAAU;EACvB,MAAM,OAAO,WAAW;EACxB,MAAM,QAAQ,SAAS;AAEvB,MAAI,CAAC,MACH;EAGF,MAAM,EAAE,OAAO,aAAa,mBAAmB,MAAM,SAAS;EAC9D,MAAM,YAAY,aAAa,MAAM,OAAO,OAAO,SAAS;EAC5D,MAAM,WAAW,cAAc,WAAW,MAAM;AAEhD,MAAI,KAAK,aAAa,CAAC,YAAY,UAAU,SAAS,GAAG;AACvD,SAAM,QAAQ;AACd,gBAAa,UAAU;AACvB,kBAAe,GAAG;AAClB,eAAY,GAAG;AACf,kBAAe,UAAU;AAEzB,OAAI,KAAK,YACP,MAAK,YAAY,IAAI,GAAG;AAG1B,OAAI,KAAK,gBAAgB;IACvB,MAAM,eAAe,kBAAkB,IAAI,OAAO,UAAU,KAAK;AACjE,UAAM,QAAQ;AACd,mBAAe,aAAa;;AAE9B;;AAGF,MAAI,CAAC,KAAK,kBAAkB,CAAC,UAAU;GACrC,MAAM,UAAU,kBAAkB,WAAW,OAAO,UAAU,MAAM;AACpE,SAAM,QAAQ;AACd,kBAAe,QAAQ;;IAExB,CAAC,SAAS,CAAC;CAEd,MAAM,iBAAA,GAAA,MAAA,cACH,MAAqB;EACpB,MAAM,QAAQ,EAAE;EAChB,MAAM,OAAO,WAAW;EAExB,MAAM,EAAE,OAAO,UAAU,cAAc,mBAAmB,MAAM,SAAS;EACzE,MAAM,QAAQ,MAAM,kBAAkB;EACtC,MAAM,MAAM,MAAM,gBAAgB;EAClC,MAAM,YAAY,aAAa;AAE/B,MAAI,EAAE,QAAQ,aAAa;AACzB,KAAE,gBAAgB;AAElB,OAAI,EAAE,WAAY,EAAE,WAAW,CAAC,EAAE,QAAS;IACzC,MAAM,eAAe,KAAK,IAAI,OAAO,UAAU,OAAO;AAGtD,gBADiB,eADA,WAAW,UAAU,MAAM,aAAa,EAAE,MAAM,MAAM,aAAa,CAAC,EAC3C,OAAO,UAAU,UAAU,EAC/C,EAAE;AACxB;;AAGF,OAAI,UAAU,KAAK;IACjB,MAAM,aAAa,KAAK,IAAI,KAAK,UAAU,OAAO;IAClD,MAAM,SAAS,UAAU,MAAM,GAAG,MAAM;IACxC,MAAM,WAAW,WAAW,UAAU,MAAM,WAAW,EAAE,MAAM,MAAM,WAAW,CAAC;AAOjF,gBANiB,eACf,WAAW,QAAQ,MAAM,GAAG,UAC5B,OACA,UACA,UACD,EACqB,MAAM;AAC5B;;AAGF,OAAI,UAAU,EACZ;GAGF,IAAI,YAAY,QAAQ;AACxB,UAAO,aAAa,KAAK,MAAM,cAAc,MAAM,WAAW,SAAS,UACrE;AAGF,OAAI,YAAY,EACd;AAMF,eADiB,eAFC,WAAW,UAAU,MAAM,GAAG,UAAU,EAAE,MAAM,MAAM,GAAG,UAAU,CAAC,GACrE,WAAW,UAAU,MAAM,YAAY,EAAE,EAAE,MAAM,MAAM,YAAY,EAAE,CAAC,EACjC,OAAO,UAAU,UAAU,EAC3D,UAAU;aACvB,EAAE,QAAQ,UAAU;AAC7B,KAAE,gBAAgB;AAElB,OAAI,UAAU,KAAK;IACjB,MAAM,aAAa,KAAK,IAAI,KAAK,UAAU,OAAO;IAClD,MAAM,SAAS,UAAU,MAAM,GAAG,MAAM;IACxC,MAAM,WAAW,WAAW,UAAU,MAAM,WAAW,EAAE,MAAM,MAAM,WAAW,CAAC;AAOjF,gBANiB,eACf,WAAW,QAAQ,MAAM,GAAG,UAC5B,OACA,UACA,UACD,EACqB,MAAM;AAC5B;;GAGF,IAAI,YAAY;AAChB,UACE,YAAY,MAAM,UAClB,MAAM,cACN,MAAM,WAAW,SAAS,UAE1B;AAGF,OAAI,aAAa,UAAU,OACzB;AAMF,eADiB,eAFC,WAAW,UAAU,MAAM,GAAG,MAAM,EAAE,MAAM,MAAM,GAAG,MAAM,CAAC,GAC7D,WAAW,UAAU,MAAM,YAAY,EAAE,EAAE,MAAM,MAAM,YAAY,EAAE,CAAC,EACjC,OAAO,UAAU,UAAU,EAC3D,MAAM;aACnB,EAAE,QAAQ,gBAAgB,CAAC,EAAE,UAAU;GAChD,MAAM,UAAU,yBAAyB,QAAQ,GAAG,OAAO,MAAM,MAAM;AACvE,OAAI,YAAY,QAAQ,GAAG;AACzB,MAAE,gBAAgB;AAClB,UAAM,kBAAkB,SAAS,QAAQ;;aAElC,EAAE,QAAQ,eAAe,CAAC,EAAE;OACjC,QAAQ,GAAG;IACb,MAAM,YAAY,mBAAmB,OAAO,QAAQ,EAAE;AACtD,QAAI,aAAa,KAAK,cAAc,QAAQ,GAAG;AAC7C,OAAE,gBAAgB;AAClB,WAAM,kBAAkB,YAAY,GAAG,YAAY,EAAE;;;aAGhD,EAAE,IAAI,WAAW,KAAK,CAAC,EAAE,WAAW,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ;AACtE,KAAE,gBAAgB;GAElB,IAAI,YAAY,KAAK,IAAI,OAAO,UAAU,OAAO;AACjD,UACE,YAAY,MAAM,UAClB,MAAM,cACN,MAAM,WAAW,SAAS,UAE1B;AAGF,OAAI,aAAa,MAAM,OACrB;GAGF,MAAM,OAAO,MAAM;GACnB,MAAM,KAAK,YAAY,UAAU,EAAE,IAAI,GAAG,EAAE;AAC5C,OAAI,CAAC,KAAK,QAAS,KAAK,GAAG,CACzB;GAGF,MAAM,YAAY,WAAW,UAAU,MAAM,GAAG,UAAU,EAAE,MAAM,MAAM,GAAG,UAAU,CAAC;GACtF,MAAM,WACJ,QAAQ,MACJ,WACE,UAAU,MAAM,KAAK,IAAI,KAAK,UAAU,OAAO,CAAC,EAChD,MAAM,MAAM,KAAK,IAAI,KAAK,UAAU,OAAO,CAAC,CAC7C,GACD,WAAW,UAAU,MAAM,UAAU,EAAE,MAAM,MAAM,UAAU,CAAC;GACpE,MAAM,WAAW,eAAe,YAAY,KAAK,UAAU,OAAO,UAAU,UAAU;AAEtF,eAAY,UADS,yBAAyB,YAAY,GAAG,OAAO,SAAS,CAC1C;;IAGvC,CAAC,UAAU,YAAY,CACxB;CAED,MAAM,eAAA,GAAA,MAAA,cACH,MAAsB;AACrB,IAAE,gBAAgB;EAClB,MAAM,QAAQ,EAAE;EAChB,MAAM,OAAO,WAAW;EAExB,MAAM,aAAa,EAAE,eAAe,QAAQ,OAAO,IAAI;EACvD,MAAM,QAAQ,MAAM,kBAAkB;EACtC,MAAM,MAAM,MAAM,gBAAgB;EAClC,MAAM,YAAY,aAAa;EAE/B,MAAM,EAAE,OAAO,UAAU,cAAc,mBAAmB,MAAM,GAAG;EACnE,MAAM,aAAa,KAAK,IAAI,KAAK,UAAU,OAAO;EAClD,MAAM,YAAY,WAAW,UAAU,MAAM,GAAG,MAAM,EAAE,MAAM,MAAM,GAAG,MAAM,CAAC;EAC9E,MAAM,WAAW,WAAW,UAAU,MAAM,WAAW,EAAE,MAAM,MAAM,WAAW,CAAC;EACjF,MAAM,WAAW,eACf,YAAY,aAAa,UACzB,OACA,UACA,UACD;EAED,MAAM,EAAE,gBAAgB,YAAY,SAAS;EAE7C,MAAM,cAAc,KAAK,KAAK,eAAe,UAAU,QAAQ,MAAM,OAAO;AAC5E,MAAI,UAAU,SAAS,cACrB,OAAM,kBAAkB,aAAa,YAAY;IAGrD,CAAC,YAAY,CACd;CAED,MAAM,qBAAA,GAAA,MAAA,cAAiC,UAA4B;AAGjE,MAFa,WAAW,QAEf,QACP,OAAM,aAAa,gBAAgB,OAAO;MAE1C,OAAM,gBAAgB,eAAe;IAEtC,EAAE,CAAC;CAEN,MAAM,eAAA,GAAA,MAAA,cACH,SAAkC;EACjC,MAAM,YAAY,SAAS;AAE3B,MAAI,WAAW;AACb,aAAU,oBAAoB,SAAS,YAAY;AACnD,aAAU,oBAAoB,SAAS,YAAY;AACnD,aAAU,oBAAoB,QAAQ,WAAW;AACjD,aAAU,oBAAoB,WAAW,cAAc;AACvD,aAAU,oBAAoB,WAAW,cAA+B;AACxE,aAAU,oBAAoB,SAAS,YAA6B;;AAGtE,WAAS,UAAU;AAEnB,MAAI,MAAM;AACR,QAAK,iBAAiB,SAAS,YAAY;AAC3C,QAAK,iBAAiB,SAAS,YAAY;AAC3C,QAAK,iBAAiB,QAAQ,WAAW;AACzC,QAAK,iBAAiB,WAAW,cAAc;AAC/C,QAAK,iBAAiB,WAAW,cAA+B;AAChE,QAAK,iBAAiB,SAAS,YAA6B;AAE5D,qBAAkB,KAAK;AAEvB,OAAI,QAAQ,kBAAkB,CAAC,KAAK,OAAO;IACzC,MAAM,EAAE,OAAO,aAAa,mBAAmB,SAAS,GAAG;IAC3D,MAAM,UAAU,kBAAkB,IAAI,OAAO,UAAU,KAAK;AAC5D,SAAK,QAAQ;AACb,mBAAe,QAAQ;;;IAI7B;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,EAAA,GAAA,MAAA,iBAAgB;EACd,MAAM,QAAQ,SAAS;AACvB,MAAI,CAAC,MACH;AAGF,oBAAkB,MAAM;IACvB,CAAC,QAAQ,SAAS,kBAAkB,CAAC;AAgCxC,QAAO;EACL,KAAK;EACL,OAAO;EACP;EACA,mBAlCwB;GACxB,MAAM,EAAE,UAAU,YAAY;AAC9B,UAAO,cAAc,aAAa,SAAS,MAAM;MAC/C;EAgCF,QAAA,GAAA,MAAA,mBA9B8B;GAC9B,MAAM,OAAO,WAAW;GACxB,MAAM,QAAQ,SAAS;AAEvB,gBAAa,UAAU;AACvB,kBAAe,GAAG;AAClB,eAAY,GAAG;AACf,kBAAe,UAAU;AAEzB,OAAI,MACF,KAAI,KAAK,gBAAgB;IACvB,MAAM,EAAE,OAAO,aAAa,mBAAmB,MAAM,GAAG;IACxD,MAAM,UAAU,kBAAkB,IAAI,OAAO,UAAU,KAAK;AAC5D,UAAM,QAAQ;AACd,mBAAe,QAAQ;SAEvB,OAAM,QAAQ;AAIlB,OAAI,KAAK,YACP,MAAK,YAAY,IAAI,GAAG;KAEzB,EAAE,CAAC;EAQL;;AAGH,SAAS,yBAAyB,MAAc,OAAmB,OAAuB;CACxF,IAAI,MAAM;AACV,QAAO,MAAM,MAAM,UAAU,MAAM,MAAM,UAAU,MAAM,QAAQ,MAAM,KAAK,SAAS,UACnF;AAEF,QAAO"}
|
|
1
|
+
{"version":3,"file":"use-mask.cjs","names":[],"sources":["../../src/use-mask/use-mask.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nconst DEFAULT_TOKENS: Record<string, RegExp> = {\n '9': /[0-9]/,\n a: /[A-Za-z]/,\n A: /[A-Z]/,\n '*': /[A-Za-z0-9]/,\n '#': /[-+0-9]/,\n};\n\nexport interface UseMaskOptions {\n /** Mask pattern string or array of string literals and RegExp objects */\n mask: string | Array<string | RegExp>;\n\n /** Override or extend the default token map */\n tokens?: Record<string, RegExp>;\n\n /** Called before masking on each keystroke, can return overrides for mask options */\n modify?: (\n value: string\n ) => Partial<Pick<UseMaskOptions, 'mask' | 'tokens' | 'slotChar' | 'separate'>> | undefined;\n\n /** When true, raw and display values are decoupled */\n separate?: boolean;\n\n /** Character displayed in unfilled slots, `\"_\"` by default */\n slotChar?: string | null;\n\n /** Show mask pattern even when field is empty and unfocused */\n alwaysShowMask?: boolean;\n\n /** Show mask placeholder on focus, `true` by default */\n showMaskOnFocus?: boolean;\n\n /** Transform each character before validation and insertion */\n transform?: (char: string) => string;\n\n /** Clear value on blur when mask is incomplete, `false` by default */\n autoClear?: boolean;\n\n /** Sets aria-invalid on the input */\n invalid?: boolean;\n\n /** Called on every change with raw and masked values */\n onChangeRaw?: (rawValue: string, maskedValue: string) => void;\n\n /** Called when all required mask slots are filled */\n onComplete?: (maskedValue: string, rawValue: string) => void;\n\n /** Escape hatch for advanced cursor/value manipulation */\n beforeMaskedStateChange?: (states: {\n previousState: MaskState;\n currentState: MaskState;\n nextState: MaskState;\n }) => MaskState;\n}\n\nexport interface MaskState {\n value: string;\n selection: { start: number; end: number } | null;\n}\n\nexport interface UseMaskReturnValue {\n /** Ref to attach to the input element */\n ref: React.RefCallback<HTMLInputElement>;\n\n /** Current masked display value */\n value: string;\n\n /** Current raw unmasked value */\n rawValue: string;\n\n /** Whether all required mask slots are filled */\n isComplete: boolean;\n\n /** Clear the input value and reset state */\n reset: () => void;\n}\n\ninterface MaskSlot {\n type: 'token' | 'literal';\n char: string;\n pattern?: RegExp;\n optional?: boolean;\n}\n\nfunction parseMask(\n mask: string | Array<string | RegExp>,\n tokens: Record<string, RegExp>\n): MaskSlot[] {\n if (Array.isArray(mask)) {\n return mask.map((item) => {\n if (item instanceof RegExp) {\n return { type: 'token', char: '_', pattern: item };\n }\n return { type: 'literal', char: item };\n });\n }\n\n const slots: MaskSlot[] = [];\n let optional = false;\n\n for (let i = 0; i < mask.length; i++) {\n const char = mask[i];\n\n if (char === '\\\\' && i + 1 < mask.length) {\n i++;\n slots.push({ type: 'literal', char: mask[i] });\n continue;\n }\n\n if (char === '?') {\n optional = true;\n continue;\n }\n\n if (tokens[char]) {\n slots.push({ type: 'token', char, pattern: tokens[char], optional });\n } else {\n slots.push({ type: 'literal', char, optional });\n }\n }\n\n return slots;\n}\n\nfunction getSlotChar(slotCharOption: string | null | undefined, index: number): string {\n if (slotCharOption === null || slotCharOption === '' || slotCharOption === undefined) {\n return '';\n }\n if (slotCharOption.length > 1) {\n return slotCharOption[index] || '_';\n }\n return slotCharOption;\n}\n\nfunction applyMaskToRaw(\n raw: string,\n slots: MaskSlot[],\n _slotCharOption: string | null | undefined,\n transform?: (char: string) => string\n): string {\n let result = '';\n let rawIndex = 0;\n let slotIndex = 0;\n\n for (slotIndex = 0; slotIndex < slots.length; slotIndex++) {\n const slot = slots[slotIndex];\n if (slot.type === 'literal') {\n result += slot.char;\n } else if (rawIndex < raw.length) {\n const ch = transform ? transform(raw[rawIndex]) : raw[rawIndex];\n if (slot.pattern && slot.pattern.test(ch)) {\n result += ch;\n rawIndex++;\n } else {\n rawIndex++;\n slotIndex--;\n }\n } else {\n break;\n }\n }\n\n return result;\n}\n\nfunction buildDisplayValue(\n value: string,\n slots: MaskSlot[],\n slotCharOption: string | null | undefined,\n showSlots: boolean\n): string {\n if (!showSlots) {\n return value;\n }\n\n let display = value;\n\n for (let i = value.length; i < slots.length; i++) {\n const slot = slots[i];\n if (slot.type === 'literal') {\n display += slot.char;\n } else {\n const sc = getSlotChar(slotCharOption, i);\n if (!sc) {\n break;\n }\n display += sc;\n }\n }\n\n return display;\n}\n\nfunction extractRaw(masked: string, slots: MaskSlot[]): string {\n let raw = '';\n for (let i = 0; i < masked.length && i < slots.length; i++) {\n if (slots[i].type === 'token') {\n raw += masked[i];\n }\n }\n return raw;\n}\n\nfunction checkComplete(masked: string, slots: MaskSlot[]): boolean {\n for (let i = 0; i < slots.length; i++) {\n if (slots[i].type === 'token' && !slots[i].optional) {\n if (i >= masked.length) {\n return false;\n }\n if (!slots[i].pattern!.test(masked[i])) {\n return false;\n }\n }\n }\n return true;\n}\n\nfunction findNextTokenIndex(slots: MaskSlot[], from: number): number {\n for (let i = from; i < slots.length; i++) {\n if (slots[i].type === 'token') {\n return i;\n }\n }\n return slots.length;\n}\n\nfunction findPrevTokenIndex(slots: MaskSlot[], from: number): number {\n for (let i = from; i >= 0; i--) {\n if (slots[i].type === 'token') {\n return i;\n }\n }\n return -1;\n}\n\nfunction processInput(\n inputValue: string,\n slots: MaskSlot[],\n _slotCharOption: string | null | undefined\n): string {\n let result = '';\n let inputIndex = 0;\n\n for (\n let slotIndex = 0;\n slotIndex < slots.length && inputIndex <= inputValue.length;\n slotIndex++\n ) {\n const slot = slots[slotIndex];\n\n if (slot.type === 'literal') {\n result += slot.char;\n if (inputIndex < inputValue.length && inputValue[inputIndex] === slot.char) {\n inputIndex++;\n }\n continue;\n }\n\n if (inputIndex >= inputValue.length) {\n break;\n }\n\n while (inputIndex < inputValue.length) {\n const ch = inputValue[inputIndex];\n inputIndex++;\n\n if (slot.pattern!.test(ch)) {\n result += ch;\n break;\n }\n }\n\n if (result.length <= slotIndex) {\n break;\n }\n }\n\n return result;\n}\n\nfunction getResolvedOptions(options: UseMaskOptions, rawValue: string) {\n const tokens = { ...DEFAULT_TOKENS, ...options.tokens };\n let mask = options.mask;\n let slotChar: string | null | undefined = options.slotChar === undefined ? '_' : options.slotChar;\n let separate = options.separate ?? false;\n\n if (options.modify) {\n const overrides = options.modify(rawValue);\n if (overrides) {\n if (overrides.mask !== undefined) {\n mask = overrides.mask;\n }\n if (overrides.tokens !== undefined) {\n Object.assign(tokens, overrides.tokens);\n }\n if (overrides.slotChar !== undefined) {\n slotChar = overrides.slotChar;\n }\n if (overrides.separate !== undefined) {\n separate = overrides.separate;\n }\n }\n }\n\n const slots = parseMask(mask, tokens);\n return { slots, slotChar, separate, tokens, transform: options.transform };\n}\n\nexport function formatMask(raw: string, options: UseMaskOptions): string {\n const { slots, slotChar, transform } = getResolvedOptions(options, raw);\n return applyMaskToRaw(raw, slots, slotChar, transform);\n}\n\nexport function unformatMask(masked: string, options: UseMaskOptions): string {\n const { slots } = getResolvedOptions(options, '');\n return extractRaw(masked, slots);\n}\n\nexport function isMaskComplete(masked: string, options: UseMaskOptions): boolean {\n const { slots } = getResolvedOptions(options, '');\n return checkComplete(masked, slots);\n}\n\nexport function generatePattern(mode: 'full' | 'full-inexact', options: UseMaskOptions): string {\n const { slots } = getResolvedOptions(options, '');\n let pattern = '';\n\n for (const slot of slots) {\n if (slot.type === 'literal') {\n pattern += slot.char.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n } else {\n const src = slot.pattern!.source;\n if (mode === 'full-inexact') {\n pattern += slot.optional ? `${src}?` : src;\n } else {\n pattern += slot.optional ? `(${src})?` : `(${src})`;\n }\n }\n }\n\n return pattern;\n}\n\nexport function useMask(options: UseMaskOptions): UseMaskReturnValue {\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const inputRef = useRef<HTMLInputElement | null>(null);\n const [maskedValue, setMaskedValue] = useState('');\n const [rawValue, setRawValue] = useState('');\n const processedRef = useRef('');\n const wasCompleteRef = useRef(false);\n const isFocusedRef = useRef(false);\n\n const getOptions = useCallback(() => {\n const opts = optionsRef.current;\n return getResolvedOptions(opts, rawValue);\n }, [rawValue]);\n\n const updateValue = useCallback(\n (newMasked: string, cursorPos?: number) => {\n const opts = optionsRef.current;\n const { slots } = getResolvedOptions(\n opts,\n extractRaw(newMasked, getResolvedOptions(opts, '').slots)\n );\n const raw = extractRaw(newMasked, slots);\n\n const { slots: resolvedSlots, slotChar } = getResolvedOptions(opts, raw);\n\n const reprocessed = processInput(newMasked, resolvedSlots, slotChar);\n const newRaw = extractRaw(reprocessed, resolvedSlots);\n\n const showSlots = opts.alwaysShowMask || isFocusedRef.current;\n const showOnFocus = opts.showMaskOnFocus !== false;\n const shouldShowSlots = showSlots && (showOnFocus || reprocessed.length > 0);\n\n const displayValue = buildDisplayValue(reprocessed, resolvedSlots, slotChar, shouldShowSlots);\n\n processedRef.current = reprocessed;\n setMaskedValue(displayValue);\n setRawValue(newRaw);\n\n if (inputRef.current) {\n inputRef.current.value = displayValue;\n if (cursorPos !== undefined && document.activeElement === inputRef.current) {\n const pos = Math.min(cursorPos, reprocessed.length);\n inputRef.current.setSelectionRange(pos, pos);\n }\n }\n\n if (opts.onChangeRaw) {\n opts.onChangeRaw(newRaw, displayValue);\n }\n\n const complete = checkComplete(reprocessed, resolvedSlots);\n if (complete && !wasCompleteRef.current && opts.onComplete) {\n opts.onComplete(displayValue, newRaw);\n }\n wasCompleteRef.current = complete;\n\n return { displayValue, newRaw, reprocessed, resolvedSlots };\n },\n [getOptions]\n );\n\n const handleInput = useCallback(\n (e: Event) => {\n const input = e.target as HTMLInputElement;\n const opts = optionsRef.current;\n\n const { slots: resolvedSlots, slotChar, transform } = getResolvedOptions(opts, '');\n const raw = extractRaw(input.value, resolvedSlots);\n const reformatted = applyMaskToRaw(raw, resolvedSlots, slotChar, transform);\n updateValue(reformatted, reformatted.length);\n },\n [updateValue]\n );\n\n const clampCursorToProcessed = useCallback((input: HTMLInputElement) => {\n const start = input.selectionStart ?? 0;\n const end = input.selectionEnd ?? 0;\n if (start !== end) {\n return;\n }\n\n const opts = optionsRef.current;\n const { slots } = getResolvedOptions(opts, '');\n const processed = processedRef.current;\n const endPos =\n processed.length > 0\n ? findNextEditablePosition(processed.length, slots, processed)\n : findNextTokenIndex(slots, 0);\n const startPos = findNextTokenIndex(slots, 0);\n\n if (start > endPos || start < startPos) {\n input.setSelectionRange(endPos, endPos);\n }\n }, []);\n\n const handleFocus = useCallback(() => {\n isFocusedRef.current = true;\n const opts = optionsRef.current;\n const input = inputRef.current;\n\n if (!input) {\n return;\n }\n\n const { slots, slotChar } = getResolvedOptions(opts, '');\n const showOnFocus = opts.showMaskOnFocus !== false;\n const processed = processedRef.current;\n\n if (showOnFocus || opts.alwaysShowMask) {\n const display = buildDisplayValue(processed, slots, slotChar, true);\n input.value = display;\n setMaskedValue(display);\n }\n\n requestAnimationFrame(() => {\n if (input === document.activeElement) {\n clampCursorToProcessed(input);\n }\n });\n }, [clampCursorToProcessed]);\n\n const handleMouseUp = useCallback(() => {\n const input = inputRef.current;\n if (!input || input !== document.activeElement) {\n return;\n }\n\n clampCursorToProcessed(input);\n }, [clampCursorToProcessed]);\n\n const handleMouseDown = useCallback(() => {\n const input = inputRef.current;\n if (!input) {\n return;\n }\n\n requestAnimationFrame(() => {\n if (input !== document.activeElement) {\n return;\n }\n\n const start = input.selectionStart ?? 0;\n const end = input.selectionEnd ?? 0;\n if (start !== end) {\n return;\n }\n\n const opts = optionsRef.current;\n const { slots } = getResolvedOptions(opts, '');\n const processed = processedRef.current;\n const endPos =\n processed.length > 0\n ? findNextEditablePosition(processed.length, slots, processed)\n : findNextTokenIndex(slots, 0);\n\n if (start > endPos) {\n input.setSelectionRange(endPos, endPos);\n }\n });\n }, []);\n\n const handleBlur = useCallback(() => {\n isFocusedRef.current = false;\n const opts = optionsRef.current;\n const input = inputRef.current;\n\n if (!input) {\n return;\n }\n\n const { slots, slotChar } = getResolvedOptions(opts, rawValue);\n const expectedFocusDisplay = buildDisplayValue(processedRef.current, slots, slotChar, true);\n const processed =\n input.value === expectedFocusDisplay\n ? processedRef.current\n : processInput(input.value, slots, slotChar);\n const complete = checkComplete(processed, slots);\n\n if (opts.autoClear && !complete && processed.length > 0) {\n input.value = '';\n processedRef.current = '';\n setMaskedValue('');\n setRawValue('');\n wasCompleteRef.current = false;\n\n if (opts.onChangeRaw) {\n opts.onChangeRaw('', '');\n }\n\n if (opts.alwaysShowMask) {\n const emptyDisplay = buildDisplayValue('', slots, slotChar, true);\n input.value = emptyDisplay;\n setMaskedValue(emptyDisplay);\n }\n return;\n }\n\n if (!opts.alwaysShowMask && !complete) {\n if (extractRaw(processed, slots).length === 0) {\n input.value = '';\n processedRef.current = '';\n setMaskedValue('');\n setRawValue('');\n wasCompleteRef.current = false;\n\n if (opts.onChangeRaw) {\n opts.onChangeRaw('', '');\n }\n return;\n }\n\n const display = buildDisplayValue(processed, slots, slotChar, false);\n input.value = display;\n setMaskedValue(display);\n }\n }, [rawValue]);\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n const input = e.target as HTMLInputElement;\n const opts = optionsRef.current;\n\n const { slots, slotChar, transform } = getResolvedOptions(opts, rawValue);\n const start = input.selectionStart ?? 0;\n const end = input.selectionEnd ?? 0;\n const processed = processedRef.current;\n\n if (e.key === 'Backspace') {\n e.preventDefault();\n\n if (e.metaKey || (e.ctrlKey && !e.altKey)) {\n const clampedStart = Math.min(start, processed.length);\n const afterRaw = extractRaw(processed.slice(clampedStart), slots.slice(clampedStart));\n const newValue = applyMaskToRaw(afterRaw, slots, slotChar, transform);\n updateValue(newValue, 0);\n return;\n }\n\n if (start !== end) {\n const clampedEnd = Math.min(end, processed.length);\n const before = processed.slice(0, start);\n const afterRaw = extractRaw(processed.slice(clampedEnd), slots.slice(clampedEnd));\n const newValue = applyMaskToRaw(\n extractRaw(before, slots) + afterRaw,\n slots,\n slotChar,\n transform\n );\n updateValue(newValue, start);\n return;\n }\n\n if (start === 0) {\n return;\n }\n\n let deletePos = start - 1;\n while (deletePos >= 0 && slots[deletePos] && slots[deletePos].type === 'literal') {\n deletePos--;\n }\n\n if (deletePos < 0) {\n return;\n }\n\n const beforeRaw = extractRaw(processed.slice(0, deletePos), slots.slice(0, deletePos));\n const afterRaw = extractRaw(processed.slice(deletePos + 1), slots.slice(deletePos + 1));\n const newValue = applyMaskToRaw(beforeRaw + afterRaw, slots, slotChar, transform);\n updateValue(newValue, deletePos);\n } else if (e.key === 'Delete') {\n e.preventDefault();\n\n if (start !== end) {\n const clampedEnd = Math.min(end, processed.length);\n const before = processed.slice(0, start);\n const afterRaw = extractRaw(processed.slice(clampedEnd), slots.slice(clampedEnd));\n const newValue = applyMaskToRaw(\n extractRaw(before, slots) + afterRaw,\n slots,\n slotChar,\n transform\n );\n updateValue(newValue, start);\n return;\n }\n\n let deletePos = start;\n while (\n deletePos < slots.length &&\n slots[deletePos] &&\n slots[deletePos].type === 'literal'\n ) {\n deletePos++;\n }\n\n if (deletePos >= processed.length) {\n return;\n }\n\n const beforeRaw = extractRaw(processed.slice(0, start), slots.slice(0, start));\n const afterRaw = extractRaw(processed.slice(deletePos + 1), slots.slice(deletePos + 1));\n const newValue = applyMaskToRaw(beforeRaw + afterRaw, slots, slotChar, transform);\n updateValue(newValue, start);\n } else if (e.key === 'ArrowRight' && !e.shiftKey) {\n const nextPos = findNextEditablePosition(start + 1, slots, input.value);\n if (nextPos !== start + 1) {\n e.preventDefault();\n input.setSelectionRange(nextPos, nextPos);\n }\n } else if (e.key === 'ArrowLeft' && !e.shiftKey) {\n if (start > 0) {\n const prevToken = findPrevTokenIndex(slots, start - 1);\n if (prevToken >= 0 && prevToken !== start - 1) {\n e.preventDefault();\n input.setSelectionRange(prevToken + 1, prevToken + 1);\n }\n }\n } else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {\n e.preventDefault();\n\n let insertPos = Math.min(start, processed.length);\n while (\n insertPos < slots.length &&\n slots[insertPos] &&\n slots[insertPos].type === 'literal'\n ) {\n insertPos++;\n }\n\n if (insertPos >= slots.length) {\n return;\n }\n\n const slot = slots[insertPos];\n const ch = transform ? transform(e.key) : e.key;\n if (!slot.pattern!.test(ch)) {\n return;\n }\n\n const beforeRaw = extractRaw(processed.slice(0, insertPos), slots.slice(0, insertPos));\n const afterRaw =\n start < end\n ? extractRaw(\n processed.slice(Math.min(end, processed.length)),\n slots.slice(Math.min(end, processed.length))\n )\n : extractRaw(processed.slice(insertPos), slots.slice(insertPos));\n const newValue = applyMaskToRaw(beforeRaw + ch + afterRaw, slots, slotChar, transform);\n const newCursorPos = findNextEditablePosition(insertPos + 1, slots, newValue);\n updateValue(newValue, newCursorPos);\n }\n },\n [rawValue, updateValue]\n );\n\n const handlePaste = useCallback(\n (e: ClipboardEvent) => {\n e.preventDefault();\n const input = e.target as HTMLInputElement;\n const opts = optionsRef.current;\n\n const pastedText = e.clipboardData?.getData('text') ?? '';\n const start = input.selectionStart ?? 0;\n const end = input.selectionEnd ?? 0;\n const processed = processedRef.current;\n\n const { slots, slotChar, transform } = getResolvedOptions(opts, '');\n const clampedEnd = Math.min(end, processed.length);\n const beforeRaw = extractRaw(processed.slice(0, start), slots.slice(0, start));\n const afterRaw = extractRaw(processed.slice(clampedEnd), slots.slice(clampedEnd));\n const newValue = applyMaskToRaw(\n beforeRaw + pastedText + afterRaw,\n slots,\n slotChar,\n transform\n );\n\n const { reprocessed } = updateValue(newValue);\n\n const pasteEndPos = Math.min((reprocessed || newValue).length, slots.length);\n if (input === document.activeElement) {\n input.setSelectionRange(pasteEndPos, pasteEndPos);\n }\n },\n [updateValue]\n );\n\n const setAriaAttributes = useCallback((input: HTMLInputElement) => {\n const opts = optionsRef.current;\n\n if (opts.invalid) {\n input.setAttribute('aria-invalid', 'true');\n } else {\n input.removeAttribute('aria-invalid');\n }\n }, []);\n\n const refCallback = useCallback(\n (node: HTMLInputElement | null) => {\n const prevInput = inputRef.current;\n\n if (prevInput) {\n prevInput.removeEventListener('input', handleInput);\n prevInput.removeEventListener('focus', handleFocus);\n prevInput.removeEventListener('blur', handleBlur);\n prevInput.removeEventListener('mousedown', handleMouseDown);\n prevInput.removeEventListener('mouseup', handleMouseUp);\n prevInput.removeEventListener('keydown', handleKeyDown as EventListener);\n prevInput.removeEventListener('paste', handlePaste as EventListener);\n }\n\n inputRef.current = node;\n\n if (node) {\n node.addEventListener('input', handleInput);\n node.addEventListener('focus', handleFocus);\n node.addEventListener('blur', handleBlur);\n node.addEventListener('mousedown', handleMouseDown);\n node.addEventListener('mouseup', handleMouseUp);\n node.addEventListener('keydown', handleKeyDown as EventListener);\n node.addEventListener('paste', handlePaste as EventListener);\n\n setAriaAttributes(node);\n\n if (options.alwaysShowMask && !node.value) {\n const { slots, slotChar } = getResolvedOptions(options, '');\n const display = buildDisplayValue('', slots, slotChar, true);\n node.value = display;\n setMaskedValue(display);\n }\n }\n },\n [\n handleInput,\n handleFocus,\n handleBlur,\n handleMouseDown,\n handleMouseUp,\n handleKeyDown,\n handlePaste,\n setAriaAttributes,\n options,\n ]\n );\n\n useEffect(() => {\n const input = inputRef.current;\n if (!input) {\n return;\n }\n\n setAriaAttributes(input);\n }, [options.invalid, setAriaAttributes]);\n\n const isComplete = (() => {\n const { slots } = getOptions();\n return checkComplete(processedRef.current, slots);\n })();\n\n const reset = useCallback(() => {\n const opts = optionsRef.current;\n const input = inputRef.current;\n\n processedRef.current = '';\n setMaskedValue('');\n setRawValue('');\n wasCompleteRef.current = false;\n\n if (input) {\n if (opts.alwaysShowMask) {\n const { slots, slotChar } = getResolvedOptions(opts, '');\n const display = buildDisplayValue('', slots, slotChar, true);\n input.value = display;\n setMaskedValue(display);\n } else {\n input.value = '';\n }\n }\n\n if (opts.onChangeRaw) {\n opts.onChangeRaw('', '');\n }\n }, []);\n\n return {\n ref: refCallback,\n value: maskedValue,\n rawValue,\n isComplete,\n reset,\n };\n}\n\nfunction findNextEditablePosition(from: number, slots: MaskSlot[], value: string): number {\n let pos = from;\n while (pos < slots.length && pos < value.length && slots[pos] && slots[pos].type === 'literal') {\n pos++;\n }\n return pos;\n}\n\nexport namespace useMask {\n export type Options = UseMaskOptions;\n export type ReturnValue = UseMaskReturnValue;\n}\n"],"mappings":";;;AAEA,MAAM,iBAAyC;CAC7C,KAAK;CACL,GAAG;CACH,GAAG;CACH,KAAK;CACL,KAAK;CACN;AA8ED,SAAS,UACP,MACA,QACY;AACZ,KAAI,MAAM,QAAQ,KAAK,CACrB,QAAO,KAAK,KAAK,SAAS;AACxB,MAAI,gBAAgB,OAClB,QAAO;GAAE,MAAM;GAAS,MAAM;GAAK,SAAS;GAAM;AAEpD,SAAO;GAAE,MAAM;GAAW,MAAM;GAAM;GACtC;CAGJ,MAAM,QAAoB,EAAE;CAC5B,IAAI,WAAW;AAEf,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,OAAO,KAAK;AAElB,MAAI,SAAS,QAAQ,IAAI,IAAI,KAAK,QAAQ;AACxC;AACA,SAAM,KAAK;IAAE,MAAM;IAAW,MAAM,KAAK;IAAI,CAAC;AAC9C;;AAGF,MAAI,SAAS,KAAK;AAChB,cAAW;AACX;;AAGF,MAAI,OAAO,MACT,OAAM,KAAK;GAAE,MAAM;GAAS;GAAM,SAAS,OAAO;GAAO;GAAU,CAAC;MAEpE,OAAM,KAAK;GAAE,MAAM;GAAW;GAAM;GAAU,CAAC;;AAInD,QAAO;;AAGT,SAAS,YAAY,gBAA2C,OAAuB;AACrF,KAAI,mBAAmB,QAAQ,mBAAmB,MAAM,mBAAmB,KAAA,EACzE,QAAO;AAET,KAAI,eAAe,SAAS,EAC1B,QAAO,eAAe,UAAU;AAElC,QAAO;;AAGT,SAAS,eACP,KACA,OACA,iBACA,WACQ;CACR,IAAI,SAAS;CACb,IAAI,WAAW;CACf,IAAI,YAAY;AAEhB,MAAK,YAAY,GAAG,YAAY,MAAM,QAAQ,aAAa;EACzD,MAAM,OAAO,MAAM;AACnB,MAAI,KAAK,SAAS,UAChB,WAAU,KAAK;WACN,WAAW,IAAI,QAAQ;GAChC,MAAM,KAAK,YAAY,UAAU,IAAI,UAAU,GAAG,IAAI;AACtD,OAAI,KAAK,WAAW,KAAK,QAAQ,KAAK,GAAG,EAAE;AACzC,cAAU;AACV;UACK;AACL;AACA;;QAGF;;AAIJ,QAAO;;AAGT,SAAS,kBACP,OACA,OACA,gBACA,WACQ;AACR,KAAI,CAAC,UACH,QAAO;CAGT,IAAI,UAAU;AAEd,MAAK,IAAI,IAAI,MAAM,QAAQ,IAAI,MAAM,QAAQ,KAAK;EAChD,MAAM,OAAO,MAAM;AACnB,MAAI,KAAK,SAAS,UAChB,YAAW,KAAK;OACX;GACL,MAAM,KAAK,YAAY,gBAAgB,EAAE;AACzC,OAAI,CAAC,GACH;AAEF,cAAW;;;AAIf,QAAO;;AAGT,SAAS,WAAW,QAAgB,OAA2B;CAC7D,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,UAAU,IAAI,MAAM,QAAQ,IACrD,KAAI,MAAM,GAAG,SAAS,QACpB,QAAO,OAAO;AAGlB,QAAO;;AAGT,SAAS,cAAc,QAAgB,OAA4B;AACjE,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,IAChC,KAAI,MAAM,GAAG,SAAS,WAAW,CAAC,MAAM,GAAG,UAAU;AACnD,MAAI,KAAK,OAAO,OACd,QAAO;AAET,MAAI,CAAC,MAAM,GAAG,QAAS,KAAK,OAAO,GAAG,CACpC,QAAO;;AAIb,QAAO;;AAGT,SAAS,mBAAmB,OAAmB,MAAsB;AACnE,MAAK,IAAI,IAAI,MAAM,IAAI,MAAM,QAAQ,IACnC,KAAI,MAAM,GAAG,SAAS,QACpB,QAAO;AAGX,QAAO,MAAM;;AAGf,SAAS,mBAAmB,OAAmB,MAAsB;AACnE,MAAK,IAAI,IAAI,MAAM,KAAK,GAAG,IACzB,KAAI,MAAM,GAAG,SAAS,QACpB,QAAO;AAGX,QAAO;;AAGT,SAAS,aACP,YACA,OACA,iBACQ;CACR,IAAI,SAAS;CACb,IAAI,aAAa;AAEjB,MACE,IAAI,YAAY,GAChB,YAAY,MAAM,UAAU,cAAc,WAAW,QACrD,aACA;EACA,MAAM,OAAO,MAAM;AAEnB,MAAI,KAAK,SAAS,WAAW;AAC3B,aAAU,KAAK;AACf,OAAI,aAAa,WAAW,UAAU,WAAW,gBAAgB,KAAK,KACpE;AAEF;;AAGF,MAAI,cAAc,WAAW,OAC3B;AAGF,SAAO,aAAa,WAAW,QAAQ;GACrC,MAAM,KAAK,WAAW;AACtB;AAEA,OAAI,KAAK,QAAS,KAAK,GAAG,EAAE;AAC1B,cAAU;AACV;;;AAIJ,MAAI,OAAO,UAAU,UACnB;;AAIJ,QAAO;;AAGT,SAAS,mBAAmB,SAAyB,UAAkB;CACrE,MAAM,SAAS;EAAE,GAAG;EAAgB,GAAG,QAAQ;EAAQ;CACvD,IAAI,OAAO,QAAQ;CACnB,IAAI,WAAsC,QAAQ,aAAa,KAAA,IAAY,MAAM,QAAQ;CACzF,IAAI,WAAW,QAAQ,YAAY;AAEnC,KAAI,QAAQ,QAAQ;EAClB,MAAM,YAAY,QAAQ,OAAO,SAAS;AAC1C,MAAI,WAAW;AACb,OAAI,UAAU,SAAS,KAAA,EACrB,QAAO,UAAU;AAEnB,OAAI,UAAU,WAAW,KAAA,EACvB,QAAO,OAAO,QAAQ,UAAU,OAAO;AAEzC,OAAI,UAAU,aAAa,KAAA,EACzB,YAAW,UAAU;AAEvB,OAAI,UAAU,aAAa,KAAA,EACzB,YAAW,UAAU;;;AAM3B,QAAO;EAAE,OADK,UAAU,MAAM,OAAO;EACrB;EAAU;EAAU;EAAQ,WAAW,QAAQ;EAAW;;AAG5E,SAAgB,WAAW,KAAa,SAAiC;CACvE,MAAM,EAAE,OAAO,UAAU,cAAc,mBAAmB,SAAS,IAAI;AACvE,QAAO,eAAe,KAAK,OAAO,UAAU,UAAU;;AAGxD,SAAgB,aAAa,QAAgB,SAAiC;CAC5E,MAAM,EAAE,UAAU,mBAAmB,SAAS,GAAG;AACjD,QAAO,WAAW,QAAQ,MAAM;;AAGlC,SAAgB,eAAe,QAAgB,SAAkC;CAC/E,MAAM,EAAE,UAAU,mBAAmB,SAAS,GAAG;AACjD,QAAO,cAAc,QAAQ,MAAM;;AAGrC,SAAgB,gBAAgB,MAA+B,SAAiC;CAC9F,MAAM,EAAE,UAAU,mBAAmB,SAAS,GAAG;CACjD,IAAI,UAAU;AAEd,MAAK,MAAM,QAAQ,MACjB,KAAI,KAAK,SAAS,UAChB,YAAW,KAAK,KAAK,QAAQ,uBAAuB,OAAO;MACtD;EACL,MAAM,MAAM,KAAK,QAAS;AAC1B,MAAI,SAAS,eACX,YAAW,KAAK,WAAW,GAAG,IAAI,KAAK;MAEvC,YAAW,KAAK,WAAW,IAAI,IAAI,MAAM,IAAI,IAAI;;AAKvD,QAAO;;AAGT,SAAgB,QAAQ,SAA6C;CACnE,MAAM,cAAA,GAAA,MAAA,QAAoB,QAAQ;AAClC,YAAW,UAAU;CAErB,MAAM,YAAA,GAAA,MAAA,QAA2C,KAAK;CACtD,MAAM,CAAC,aAAa,mBAAA,GAAA,MAAA,UAA2B,GAAG;CAClD,MAAM,CAAC,UAAU,gBAAA,GAAA,MAAA,UAAwB,GAAG;CAC5C,MAAM,gBAAA,GAAA,MAAA,QAAsB,GAAG;CAC/B,MAAM,kBAAA,GAAA,MAAA,QAAwB,MAAM;CACpC,MAAM,gBAAA,GAAA,MAAA,QAAsB,MAAM;CAElC,MAAM,cAAA,GAAA,MAAA,mBAA+B;EACnC,MAAM,OAAO,WAAW;AACxB,SAAO,mBAAmB,MAAM,SAAS;IACxC,CAAC,SAAS,CAAC;CAEd,MAAM,eAAA,GAAA,MAAA,cACH,WAAmB,cAAuB;EACzC,MAAM,OAAO,WAAW;EACxB,MAAM,EAAE,UAAU,mBAChB,MACA,WAAW,WAAW,mBAAmB,MAAM,GAAG,CAAC,MAAM,CAC1D;EAGD,MAAM,EAAE,OAAO,eAAe,aAAa,mBAAmB,MAFlD,WAAW,WAAW,MAAM,CAEgC;EAExE,MAAM,cAAc,aAAa,WAAW,eAAe,SAAS;EACpE,MAAM,SAAS,WAAW,aAAa,cAAc;EAErD,MAAM,YAAY,KAAK,kBAAkB,aAAa;EACtD,MAAM,cAAc,KAAK,oBAAoB;EAG7C,MAAM,eAAe,kBAAkB,aAAa,eAAe,UAF3C,cAAc,eAAe,YAAY,SAAS,GAEmB;AAE7F,eAAa,UAAU;AACvB,iBAAe,aAAa;AAC5B,cAAY,OAAO;AAEnB,MAAI,SAAS,SAAS;AACpB,YAAS,QAAQ,QAAQ;AACzB,OAAI,cAAc,KAAA,KAAa,SAAS,kBAAkB,SAAS,SAAS;IAC1E,MAAM,MAAM,KAAK,IAAI,WAAW,YAAY,OAAO;AACnD,aAAS,QAAQ,kBAAkB,KAAK,IAAI;;;AAIhD,MAAI,KAAK,YACP,MAAK,YAAY,QAAQ,aAAa;EAGxC,MAAM,WAAW,cAAc,aAAa,cAAc;AAC1D,MAAI,YAAY,CAAC,eAAe,WAAW,KAAK,WAC9C,MAAK,WAAW,cAAc,OAAO;AAEvC,iBAAe,UAAU;AAEzB,SAAO;GAAE;GAAc;GAAQ;GAAa;GAAe;IAE7D,CAAC,WAAW,CACb;CAED,MAAM,eAAA,GAAA,MAAA,cACH,MAAa;EACZ,MAAM,QAAQ,EAAE;EAChB,MAAM,OAAO,WAAW;EAExB,MAAM,EAAE,OAAO,eAAe,UAAU,cAAc,mBAAmB,MAAM,GAAG;EAElF,MAAM,cAAc,eADR,WAAW,MAAM,OAAO,cAAc,EACV,eAAe,UAAU,UAAU;AAC3E,cAAY,aAAa,YAAY,OAAO;IAE9C,CAAC,YAAY,CACd;CAED,MAAM,0BAAA,GAAA,MAAA,cAAsC,UAA4B;EACtE,MAAM,QAAQ,MAAM,kBAAkB;AAEtC,MAAI,WADQ,MAAM,gBAAgB,GAEhC;EAGF,MAAM,OAAO,WAAW;EACxB,MAAM,EAAE,UAAU,mBAAmB,MAAM,GAAG;EAC9C,MAAM,YAAY,aAAa;EAC/B,MAAM,SACJ,UAAU,SAAS,IACf,yBAAyB,UAAU,QAAQ,OAAO,UAAU,GAC5D,mBAAmB,OAAO,EAAE;EAClC,MAAM,WAAW,mBAAmB,OAAO,EAAE;AAE7C,MAAI,QAAQ,UAAU,QAAQ,SAC5B,OAAM,kBAAkB,QAAQ,OAAO;IAExC,EAAE,CAAC;CAEN,MAAM,eAAA,GAAA,MAAA,mBAAgC;AACpC,eAAa,UAAU;EACvB,MAAM,OAAO,WAAW;EACxB,MAAM,QAAQ,SAAS;AAEvB,MAAI,CAAC,MACH;EAGF,MAAM,EAAE,OAAO,aAAa,mBAAmB,MAAM,GAAG;EACxD,MAAM,cAAc,KAAK,oBAAoB;EAC7C,MAAM,YAAY,aAAa;AAE/B,MAAI,eAAe,KAAK,gBAAgB;GACtC,MAAM,UAAU,kBAAkB,WAAW,OAAO,UAAU,KAAK;AACnE,SAAM,QAAQ;AACd,kBAAe,QAAQ;;AAGzB,8BAA4B;AAC1B,OAAI,UAAU,SAAS,cACrB,wBAAuB,MAAM;IAE/B;IACD,CAAC,uBAAuB,CAAC;CAE5B,MAAM,iBAAA,GAAA,MAAA,mBAAkC;EACtC,MAAM,QAAQ,SAAS;AACvB,MAAI,CAAC,SAAS,UAAU,SAAS,cAC/B;AAGF,yBAAuB,MAAM;IAC5B,CAAC,uBAAuB,CAAC;CAE5B,MAAM,mBAAA,GAAA,MAAA,mBAAoC;EACxC,MAAM,QAAQ,SAAS;AACvB,MAAI,CAAC,MACH;AAGF,8BAA4B;AAC1B,OAAI,UAAU,SAAS,cACrB;GAGF,MAAM,QAAQ,MAAM,kBAAkB;AAEtC,OAAI,WADQ,MAAM,gBAAgB,GAEhC;GAGF,MAAM,OAAO,WAAW;GACxB,MAAM,EAAE,UAAU,mBAAmB,MAAM,GAAG;GAC9C,MAAM,YAAY,aAAa;GAC/B,MAAM,SACJ,UAAU,SAAS,IACf,yBAAyB,UAAU,QAAQ,OAAO,UAAU,GAC5D,mBAAmB,OAAO,EAAE;AAElC,OAAI,QAAQ,OACV,OAAM,kBAAkB,QAAQ,OAAO;IAEzC;IACD,EAAE,CAAC;CAEN,MAAM,cAAA,GAAA,MAAA,mBAA+B;AACnC,eAAa,UAAU;EACvB,MAAM,OAAO,WAAW;EACxB,MAAM,QAAQ,SAAS;AAEvB,MAAI,CAAC,MACH;EAGF,MAAM,EAAE,OAAO,aAAa,mBAAmB,MAAM,SAAS;EAC9D,MAAM,uBAAuB,kBAAkB,aAAa,SAAS,OAAO,UAAU,KAAK;EAC3F,MAAM,YACJ,MAAM,UAAU,uBACZ,aAAa,UACb,aAAa,MAAM,OAAO,OAAO,SAAS;EAChD,MAAM,WAAW,cAAc,WAAW,MAAM;AAEhD,MAAI,KAAK,aAAa,CAAC,YAAY,UAAU,SAAS,GAAG;AACvD,SAAM,QAAQ;AACd,gBAAa,UAAU;AACvB,kBAAe,GAAG;AAClB,eAAY,GAAG;AACf,kBAAe,UAAU;AAEzB,OAAI,KAAK,YACP,MAAK,YAAY,IAAI,GAAG;AAG1B,OAAI,KAAK,gBAAgB;IACvB,MAAM,eAAe,kBAAkB,IAAI,OAAO,UAAU,KAAK;AACjE,UAAM,QAAQ;AACd,mBAAe,aAAa;;AAE9B;;AAGF,MAAI,CAAC,KAAK,kBAAkB,CAAC,UAAU;AACrC,OAAI,WAAW,WAAW,MAAM,CAAC,WAAW,GAAG;AAC7C,UAAM,QAAQ;AACd,iBAAa,UAAU;AACvB,mBAAe,GAAG;AAClB,gBAAY,GAAG;AACf,mBAAe,UAAU;AAEzB,QAAI,KAAK,YACP,MAAK,YAAY,IAAI,GAAG;AAE1B;;GAGF,MAAM,UAAU,kBAAkB,WAAW,OAAO,UAAU,MAAM;AACpE,SAAM,QAAQ;AACd,kBAAe,QAAQ;;IAExB,CAAC,SAAS,CAAC;CAEd,MAAM,iBAAA,GAAA,MAAA,cACH,MAAqB;EACpB,MAAM,QAAQ,EAAE;EAChB,MAAM,OAAO,WAAW;EAExB,MAAM,EAAE,OAAO,UAAU,cAAc,mBAAmB,MAAM,SAAS;EACzE,MAAM,QAAQ,MAAM,kBAAkB;EACtC,MAAM,MAAM,MAAM,gBAAgB;EAClC,MAAM,YAAY,aAAa;AAE/B,MAAI,EAAE,QAAQ,aAAa;AACzB,KAAE,gBAAgB;AAElB,OAAI,EAAE,WAAY,EAAE,WAAW,CAAC,EAAE,QAAS;IACzC,MAAM,eAAe,KAAK,IAAI,OAAO,UAAU,OAAO;AAGtD,gBADiB,eADA,WAAW,UAAU,MAAM,aAAa,EAAE,MAAM,MAAM,aAAa,CAAC,EAC3C,OAAO,UAAU,UAAU,EAC/C,EAAE;AACxB;;AAGF,OAAI,UAAU,KAAK;IACjB,MAAM,aAAa,KAAK,IAAI,KAAK,UAAU,OAAO;IAClD,MAAM,SAAS,UAAU,MAAM,GAAG,MAAM;IACxC,MAAM,WAAW,WAAW,UAAU,MAAM,WAAW,EAAE,MAAM,MAAM,WAAW,CAAC;AAOjF,gBANiB,eACf,WAAW,QAAQ,MAAM,GAAG,UAC5B,OACA,UACA,UACD,EACqB,MAAM;AAC5B;;AAGF,OAAI,UAAU,EACZ;GAGF,IAAI,YAAY,QAAQ;AACxB,UAAO,aAAa,KAAK,MAAM,cAAc,MAAM,WAAW,SAAS,UACrE;AAGF,OAAI,YAAY,EACd;AAMF,eADiB,eAFC,WAAW,UAAU,MAAM,GAAG,UAAU,EAAE,MAAM,MAAM,GAAG,UAAU,CAAC,GACrE,WAAW,UAAU,MAAM,YAAY,EAAE,EAAE,MAAM,MAAM,YAAY,EAAE,CAAC,EACjC,OAAO,UAAU,UAAU,EAC3D,UAAU;aACvB,EAAE,QAAQ,UAAU;AAC7B,KAAE,gBAAgB;AAElB,OAAI,UAAU,KAAK;IACjB,MAAM,aAAa,KAAK,IAAI,KAAK,UAAU,OAAO;IAClD,MAAM,SAAS,UAAU,MAAM,GAAG,MAAM;IACxC,MAAM,WAAW,WAAW,UAAU,MAAM,WAAW,EAAE,MAAM,MAAM,WAAW,CAAC;AAOjF,gBANiB,eACf,WAAW,QAAQ,MAAM,GAAG,UAC5B,OACA,UACA,UACD,EACqB,MAAM;AAC5B;;GAGF,IAAI,YAAY;AAChB,UACE,YAAY,MAAM,UAClB,MAAM,cACN,MAAM,WAAW,SAAS,UAE1B;AAGF,OAAI,aAAa,UAAU,OACzB;AAMF,eADiB,eAFC,WAAW,UAAU,MAAM,GAAG,MAAM,EAAE,MAAM,MAAM,GAAG,MAAM,CAAC,GAC7D,WAAW,UAAU,MAAM,YAAY,EAAE,EAAE,MAAM,MAAM,YAAY,EAAE,CAAC,EACjC,OAAO,UAAU,UAAU,EAC3D,MAAM;aACnB,EAAE,QAAQ,gBAAgB,CAAC,EAAE,UAAU;GAChD,MAAM,UAAU,yBAAyB,QAAQ,GAAG,OAAO,MAAM,MAAM;AACvE,OAAI,YAAY,QAAQ,GAAG;AACzB,MAAE,gBAAgB;AAClB,UAAM,kBAAkB,SAAS,QAAQ;;aAElC,EAAE,QAAQ,eAAe,CAAC,EAAE;OACjC,QAAQ,GAAG;IACb,MAAM,YAAY,mBAAmB,OAAO,QAAQ,EAAE;AACtD,QAAI,aAAa,KAAK,cAAc,QAAQ,GAAG;AAC7C,OAAE,gBAAgB;AAClB,WAAM,kBAAkB,YAAY,GAAG,YAAY,EAAE;;;aAGhD,EAAE,IAAI,WAAW,KAAK,CAAC,EAAE,WAAW,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ;AACtE,KAAE,gBAAgB;GAElB,IAAI,YAAY,KAAK,IAAI,OAAO,UAAU,OAAO;AACjD,UACE,YAAY,MAAM,UAClB,MAAM,cACN,MAAM,WAAW,SAAS,UAE1B;AAGF,OAAI,aAAa,MAAM,OACrB;GAGF,MAAM,OAAO,MAAM;GACnB,MAAM,KAAK,YAAY,UAAU,EAAE,IAAI,GAAG,EAAE;AAC5C,OAAI,CAAC,KAAK,QAAS,KAAK,GAAG,CACzB;GAGF,MAAM,YAAY,WAAW,UAAU,MAAM,GAAG,UAAU,EAAE,MAAM,MAAM,GAAG,UAAU,CAAC;GACtF,MAAM,WACJ,QAAQ,MACJ,WACE,UAAU,MAAM,KAAK,IAAI,KAAK,UAAU,OAAO,CAAC,EAChD,MAAM,MAAM,KAAK,IAAI,KAAK,UAAU,OAAO,CAAC,CAC7C,GACD,WAAW,UAAU,MAAM,UAAU,EAAE,MAAM,MAAM,UAAU,CAAC;GACpE,MAAM,WAAW,eAAe,YAAY,KAAK,UAAU,OAAO,UAAU,UAAU;AAEtF,eAAY,UADS,yBAAyB,YAAY,GAAG,OAAO,SAAS,CAC1C;;IAGvC,CAAC,UAAU,YAAY,CACxB;CAED,MAAM,eAAA,GAAA,MAAA,cACH,MAAsB;AACrB,IAAE,gBAAgB;EAClB,MAAM,QAAQ,EAAE;EAChB,MAAM,OAAO,WAAW;EAExB,MAAM,aAAa,EAAE,eAAe,QAAQ,OAAO,IAAI;EACvD,MAAM,QAAQ,MAAM,kBAAkB;EACtC,MAAM,MAAM,MAAM,gBAAgB;EAClC,MAAM,YAAY,aAAa;EAE/B,MAAM,EAAE,OAAO,UAAU,cAAc,mBAAmB,MAAM,GAAG;EACnE,MAAM,aAAa,KAAK,IAAI,KAAK,UAAU,OAAO;EAClD,MAAM,YAAY,WAAW,UAAU,MAAM,GAAG,MAAM,EAAE,MAAM,MAAM,GAAG,MAAM,CAAC;EAC9E,MAAM,WAAW,WAAW,UAAU,MAAM,WAAW,EAAE,MAAM,MAAM,WAAW,CAAC;EACjF,MAAM,WAAW,eACf,YAAY,aAAa,UACzB,OACA,UACA,UACD;EAED,MAAM,EAAE,gBAAgB,YAAY,SAAS;EAE7C,MAAM,cAAc,KAAK,KAAK,eAAe,UAAU,QAAQ,MAAM,OAAO;AAC5E,MAAI,UAAU,SAAS,cACrB,OAAM,kBAAkB,aAAa,YAAY;IAGrD,CAAC,YAAY,CACd;CAED,MAAM,qBAAA,GAAA,MAAA,cAAiC,UAA4B;AAGjE,MAFa,WAAW,QAEf,QACP,OAAM,aAAa,gBAAgB,OAAO;MAE1C,OAAM,gBAAgB,eAAe;IAEtC,EAAE,CAAC;CAEN,MAAM,eAAA,GAAA,MAAA,cACH,SAAkC;EACjC,MAAM,YAAY,SAAS;AAE3B,MAAI,WAAW;AACb,aAAU,oBAAoB,SAAS,YAAY;AACnD,aAAU,oBAAoB,SAAS,YAAY;AACnD,aAAU,oBAAoB,QAAQ,WAAW;AACjD,aAAU,oBAAoB,aAAa,gBAAgB;AAC3D,aAAU,oBAAoB,WAAW,cAAc;AACvD,aAAU,oBAAoB,WAAW,cAA+B;AACxE,aAAU,oBAAoB,SAAS,YAA6B;;AAGtE,WAAS,UAAU;AAEnB,MAAI,MAAM;AACR,QAAK,iBAAiB,SAAS,YAAY;AAC3C,QAAK,iBAAiB,SAAS,YAAY;AAC3C,QAAK,iBAAiB,QAAQ,WAAW;AACzC,QAAK,iBAAiB,aAAa,gBAAgB;AACnD,QAAK,iBAAiB,WAAW,cAAc;AAC/C,QAAK,iBAAiB,WAAW,cAA+B;AAChE,QAAK,iBAAiB,SAAS,YAA6B;AAE5D,qBAAkB,KAAK;AAEvB,OAAI,QAAQ,kBAAkB,CAAC,KAAK,OAAO;IACzC,MAAM,EAAE,OAAO,aAAa,mBAAmB,SAAS,GAAG;IAC3D,MAAM,UAAU,kBAAkB,IAAI,OAAO,UAAU,KAAK;AAC5D,SAAK,QAAQ;AACb,mBAAe,QAAQ;;;IAI7B;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,EAAA,GAAA,MAAA,iBAAgB;EACd,MAAM,QAAQ,SAAS;AACvB,MAAI,CAAC,MACH;AAGF,oBAAkB,MAAM;IACvB,CAAC,QAAQ,SAAS,kBAAkB,CAAC;AAgCxC,QAAO;EACL,KAAK;EACL,OAAO;EACP;EACA,mBAlCwB;GACxB,MAAM,EAAE,UAAU,YAAY;AAC9B,UAAO,cAAc,aAAa,SAAS,MAAM;MAC/C;EAgCF,QAAA,GAAA,MAAA,mBA9B8B;GAC9B,MAAM,OAAO,WAAW;GACxB,MAAM,QAAQ,SAAS;AAEvB,gBAAa,UAAU;AACvB,kBAAe,GAAG;AAClB,eAAY,GAAG;AACf,kBAAe,UAAU;AAEzB,OAAI,MACF,KAAI,KAAK,gBAAgB;IACvB,MAAM,EAAE,OAAO,aAAa,mBAAmB,MAAM,GAAG;IACxD,MAAM,UAAU,kBAAkB,IAAI,OAAO,UAAU,KAAK;AAC5D,UAAM,QAAQ;AACd,mBAAe,QAAQ;SAEvB,OAAM,QAAQ;AAIlB,OAAI,KAAK,YACP,MAAK,YAAY,IAAI,GAAG;KAEzB,EAAE,CAAC;EAQL;;AAGH,SAAS,yBAAyB,MAAc,OAAmB,OAAuB;CACxF,IAAI,MAAM;AACV,QAAO,MAAM,MAAM,UAAU,MAAM,MAAM,UAAU,MAAM,QAAQ,MAAM,KAAK,SAAS,UACnF;AAEF,QAAO"}
|