@mantine/hooks 9.6.0 → 9.6.2
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/use-collapse/use-collapse.cjs +33 -6
- package/cjs/use-collapse/use-collapse.cjs.map +1 -1
- package/cjs/use-collapse/use-horizontal-collapse.cjs +30 -6
- package/cjs/use-collapse/use-horizontal-collapse.cjs.map +1 -1
- package/cjs/use-did-update/use-did-update.cjs +17 -5
- package/cjs/use-did-update/use-did-update.cjs.map +1 -1
- package/cjs/use-interval/use-interval.cjs +5 -9
- package/cjs/use-interval/use-interval.cjs.map +1 -1
- package/cjs/use-mask/use-mask.cjs +4 -2
- package/cjs/use-mask/use-mask.cjs.map +1 -1
- package/cjs/use-scroll-spy/use-scroll-spy.cjs +9 -3
- package/cjs/use-scroll-spy/use-scroll-spy.cjs.map +1 -1
- package/esm/use-collapse/use-collapse.mjs +33 -7
- package/esm/use-collapse/use-collapse.mjs.map +1 -1
- package/esm/use-collapse/use-horizontal-collapse.mjs +30 -6
- package/esm/use-collapse/use-horizontal-collapse.mjs.map +1 -1
- package/esm/use-did-update/use-did-update.mjs +17 -5
- package/esm/use-did-update/use-did-update.mjs.map +1 -1
- package/esm/use-interval/use-interval.mjs +5 -9
- package/esm/use-interval/use-interval.mjs.map +1 -1
- package/esm/use-mask/use-mask.mjs +4 -2
- package/esm/use-mask/use-mask.mjs.map +1 -1
- package/esm/use-scroll-spy/use-scroll-spy.mjs +9 -3
- package/esm/use-scroll-spy/use-scroll-spy.mjs.map +1 -1
- package/lib/use-collapse/use-collapse.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-mask.cjs","names":["useRef","useState","useCallback"],"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\ninterface UndoState {\n rawValue: string;\n selectionStart: number;\n}\n\nconst MAX_UNDO_HISTORY = 100;\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 displayValueRef = useRef('');\n const rawValueRef = useRef('');\n const wasCompleteRef = useRef(false);\n const isFocusedRef = useRef(false);\n const undoStackRef = useRef<UndoState[]>([]);\n const redoStackRef = useRef<UndoState[]>([]);\n\n const getOptions = useCallback(() => {\n const opts = optionsRef.current;\n return getResolvedOptions(opts, rawValue);\n }, [rawValue]);\n\n const applyValue = useCallback(\n ({\n reprocessed,\n newRaw,\n displayValue,\n resolvedSlots,\n cursorPos,\n notifyChange,\n }: {\n reprocessed: string;\n newRaw: string;\n displayValue: string;\n resolvedSlots: MaskSlot[];\n cursorPos?: number;\n notifyChange: boolean;\n }) => {\n const opts = optionsRef.current;\n\n processedRef.current = reprocessed;\n displayValueRef.current = displayValue;\n rawValueRef.current = newRaw;\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 (notifyChange && opts.onChangeRaw) {\n opts.onChangeRaw(newRaw, displayValue);\n }\n\n const complete = checkComplete(reprocessed, resolvedSlots);\n if (notifyChange && complete && !wasCompleteRef.current && opts.onComplete) {\n opts.onComplete(displayValue, newRaw);\n }\n wasCompleteRef.current = complete;\n },\n []\n );\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 applyValue({\n reprocessed,\n newRaw,\n displayValue,\n resolvedSlots,\n cursorPos,\n notifyChange: true,\n });\n\n return { displayValue, newRaw, reprocessed, resolvedSlots };\n },\n [applyValue, getOptions]\n );\n\n const initializeInputValue = useCallback(\n (node: HTMLInputElement) => {\n const opts = optionsRef.current;\n\n if (!node.value) {\n return false;\n }\n\n const { slots: initialSlots, slotChar: initialSlotChar } = getResolvedOptions(opts, '');\n const initialProcessed = processInput(node.value, initialSlots, initialSlotChar);\n const initialRaw = extractRaw(initialProcessed, initialSlots);\n const { slots: resolvedSlots, slotChar } = getResolvedOptions(opts, initialRaw);\n const reprocessed = processInput(node.value, resolvedSlots, slotChar);\n const newRaw = extractRaw(reprocessed, resolvedSlots);\n const showSlots = opts.alwaysShowMask || isFocusedRef.current;\n const showOnFocus = opts.showMaskOnFocus !== false;\n const shouldShowSlots = showSlots && (showOnFocus || reprocessed.length > 0);\n const displayValue = buildDisplayValue(reprocessed, resolvedSlots, slotChar, shouldShowSlots);\n\n applyValue({\n reprocessed,\n newRaw,\n displayValue,\n resolvedSlots,\n notifyChange: false,\n });\n\n return true;\n },\n [applyValue]\n );\n\n const pushUndoState = useCallback(() => {\n const input = inputRef.current;\n const selectionStart = input?.selectionStart ?? rawValueRef.current.length;\n const state: UndoState = {\n rawValue: rawValueRef.current,\n selectionStart,\n };\n const stack = undoStackRef.current;\n const top = stack[stack.length - 1];\n if (top && top.rawValue === state.rawValue && top.selectionStart === state.selectionStart) {\n return;\n }\n stack.push(state);\n if (stack.length > MAX_UNDO_HISTORY) {\n stack.shift();\n }\n redoStackRef.current = [];\n }, []);\n\n const applyHistoryState = useCallback(\n (target: UndoState) => {\n const opts = optionsRef.current;\n const { slots, slotChar, transform } = getResolvedOptions(opts, target.rawValue);\n const newMasked = applyMaskToRaw(target.rawValue, slots, slotChar, transform);\n updateValue(newMasked, target.selectionStart);\n },\n [updateValue]\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 prev = displayValueRef.current;\n const curr = input.value;\n\n let prefixLen = 0;\n const maxPrefix = Math.min(prev.length, curr.length);\n while (prefixLen < maxPrefix && prev[prefixLen] === curr[prefixLen]) {\n prefixLen++;\n }\n\n let suffixLen = 0;\n const maxSuffix = Math.min(prev.length - prefixLen, curr.length - prefixLen);\n while (\n suffixLen < maxSuffix &&\n prev[prev.length - 1 - suffixLen] === curr[curr.length - 1 - suffixLen]\n ) {\n suffixLen++;\n }\n\n const insertedText = curr.slice(prefixLen, curr.length - suffixLen);\n const removedEnd = prev.length - suffixLen;\n\n const beforeRaw = extractRaw(prev.slice(0, prefixLen), resolvedSlots.slice(0, prefixLen));\n const afterRaw = extractRaw(prev.slice(removedEnd), resolvedSlots.slice(removedEnd));\n const reformatted = applyMaskToRaw(\n beforeRaw + insertedText + afterRaw,\n resolvedSlots,\n slotChar,\n transform\n );\n const maskedPrefix = applyMaskToRaw(\n beforeRaw + insertedText,\n resolvedSlots,\n slotChar,\n transform\n );\n\n if (reformatted !== prev) {\n pushUndoState();\n }\n updateValue(reformatted, maskedPrefix.length);\n },\n [pushUndoState, 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 displayValueRef.current = 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 displayValueRef.current = '';\n rawValueRef.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 displayValueRef.current = 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 displayValueRef.current = '';\n rawValueRef.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 displayValueRef.current = 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 const modifier = e.metaKey || (e.ctrlKey && !e.altKey);\n const key = e.key.toLowerCase();\n\n if (modifier && key === 'z' && !e.shiftKey) {\n e.preventDefault();\n const prev = undoStackRef.current.pop();\n if (!prev) {\n return;\n }\n redoStackRef.current.push({\n rawValue: rawValueRef.current,\n selectionStart: input.selectionStart ?? 0,\n });\n applyHistoryState(prev);\n return;\n }\n\n if (modifier && ((key === 'z' && e.shiftKey) || (key === 'y' && !e.shiftKey))) {\n e.preventDefault();\n const next = redoStackRef.current.pop();\n if (!next) {\n return;\n }\n undoStackRef.current.push({\n rawValue: rawValueRef.current,\n selectionStart: input.selectionStart ?? 0,\n });\n applyHistoryState(next);\n return;\n }\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 pushUndoState();\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 pushUndoState();\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 pushUndoState();\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 pushUndoState();\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 pushUndoState();\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 pushUndoState();\n updateValue(newValue, newCursorPos);\n }\n },\n [applyHistoryState, pushUndoState, 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 clampedStart = Math.min(start, processed.length);\n const clampedEnd = Math.min(end, processed.length);\n const beforeRaw = extractRaw(processed.slice(0, clampedStart), slots.slice(0, clampedStart));\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 pushUndoState();\n updateValue(newValue);\n\n const maskedPrefix = applyMaskToRaw(beforeRaw + pastedText, slots, slotChar, transform);\n const pasteEndPos = Math.min(maskedPrefix.length, slots.length);\n if (input === document.activeElement) {\n input.setSelectionRange(pasteEndPos, pasteEndPos);\n }\n },\n [pushUndoState, 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 const hasInitialValue = initializeInputValue(node);\n\n if (options.alwaysShowMask && !hasInitialValue) {\n const { slots, slotChar } = getResolvedOptions(options, '');\n const display = buildDisplayValue('', slots, slotChar, true);\n node.value = display;\n displayValueRef.current = display;\n setMaskedValue(display);\n }\n }\n },\n [\n handleInput,\n handleFocus,\n handleBlur,\n handleMouseDown,\n handleMouseUp,\n handleKeyDown,\n handlePaste,\n initializeInputValue,\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 displayValueRef.current = '';\n rawValueRef.current = '';\n undoStackRef.current = [];\n redoStackRef.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 displayValueRef.current = 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":"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"}
|
|
1
|
+
{"version":3,"file":"use-mask.cjs","names":["useRef","useState","useCallback"],"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\ninterface UndoState {\n rawValue: string;\n selectionStart: number;\n}\n\nconst MAX_UNDO_HISTORY = 100;\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 displayValueRef = useRef('');\n const rawValueRef = useRef('');\n const wasCompleteRef = useRef(false);\n const isFocusedRef = useRef(false);\n const undoStackRef = useRef<UndoState[]>([]);\n const redoStackRef = useRef<UndoState[]>([]);\n\n const getOptions = useCallback(() => {\n const opts = optionsRef.current;\n return getResolvedOptions(opts, rawValue);\n }, [rawValue]);\n\n const applyValue = useCallback(\n ({\n reprocessed,\n newRaw,\n displayValue,\n resolvedSlots,\n cursorPos,\n notifyChange,\n }: {\n reprocessed: string;\n newRaw: string;\n displayValue: string;\n resolvedSlots: MaskSlot[];\n cursorPos?: number;\n notifyChange: boolean;\n }) => {\n const opts = optionsRef.current;\n\n processedRef.current = reprocessed;\n displayValueRef.current = displayValue;\n rawValueRef.current = newRaw;\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 (notifyChange && opts.onChangeRaw) {\n opts.onChangeRaw(newRaw, displayValue);\n }\n\n const complete = checkComplete(reprocessed, resolvedSlots);\n if (notifyChange && complete && !wasCompleteRef.current && opts.onComplete) {\n opts.onComplete(displayValue, newRaw);\n }\n wasCompleteRef.current = complete;\n },\n []\n );\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 applyValue({\n reprocessed,\n newRaw,\n displayValue,\n resolvedSlots,\n cursorPos,\n notifyChange: true,\n });\n\n return { displayValue, newRaw, reprocessed, resolvedSlots };\n },\n [applyValue, getOptions]\n );\n\n const initializeInputValue = useCallback(\n (node: HTMLInputElement) => {\n const opts = optionsRef.current;\n\n if (!node.value) {\n return false;\n }\n\n const isOwnValue = node.value === displayValueRef.current;\n const parse = (slots: MaskSlot[], slotCharOption: string | null | undefined) =>\n isOwnValue\n ? applyMaskToRaw(rawValueRef.current, slots, slotCharOption)\n : processInput(node.value, slots, slotCharOption);\n\n const { slots: initialSlots, slotChar: initialSlotChar } = getResolvedOptions(opts, '');\n const initialProcessed = parse(initialSlots, initialSlotChar);\n const initialRaw = extractRaw(initialProcessed, initialSlots);\n const { slots: resolvedSlots, slotChar } = getResolvedOptions(opts, initialRaw);\n const reprocessed = parse(resolvedSlots, slotChar);\n const newRaw = extractRaw(reprocessed, resolvedSlots);\n const showSlots = opts.alwaysShowMask || isFocusedRef.current;\n const showOnFocus = opts.showMaskOnFocus !== false;\n const shouldShowSlots = showSlots && (showOnFocus || reprocessed.length > 0);\n const displayValue = buildDisplayValue(reprocessed, resolvedSlots, slotChar, shouldShowSlots);\n\n applyValue({\n reprocessed,\n newRaw,\n displayValue,\n resolvedSlots,\n notifyChange: false,\n });\n\n return true;\n },\n [applyValue]\n );\n\n const pushUndoState = useCallback(() => {\n const input = inputRef.current;\n const selectionStart = input?.selectionStart ?? rawValueRef.current.length;\n const state: UndoState = {\n rawValue: rawValueRef.current,\n selectionStart,\n };\n const stack = undoStackRef.current;\n const top = stack[stack.length - 1];\n if (top && top.rawValue === state.rawValue && top.selectionStart === state.selectionStart) {\n return;\n }\n stack.push(state);\n if (stack.length > MAX_UNDO_HISTORY) {\n stack.shift();\n }\n redoStackRef.current = [];\n }, []);\n\n const applyHistoryState = useCallback(\n (target: UndoState) => {\n const opts = optionsRef.current;\n const { slots, slotChar, transform } = getResolvedOptions(opts, target.rawValue);\n const newMasked = applyMaskToRaw(target.rawValue, slots, slotChar, transform);\n updateValue(newMasked, target.selectionStart);\n },\n [updateValue]\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 prev = displayValueRef.current;\n const curr = input.value;\n\n let prefixLen = 0;\n const maxPrefix = Math.min(prev.length, curr.length);\n while (prefixLen < maxPrefix && prev[prefixLen] === curr[prefixLen]) {\n prefixLen++;\n }\n\n let suffixLen = 0;\n const maxSuffix = Math.min(prev.length - prefixLen, curr.length - prefixLen);\n while (\n suffixLen < maxSuffix &&\n prev[prev.length - 1 - suffixLen] === curr[curr.length - 1 - suffixLen]\n ) {\n suffixLen++;\n }\n\n const insertedText = curr.slice(prefixLen, curr.length - suffixLen);\n const removedEnd = prev.length - suffixLen;\n\n const beforeRaw = extractRaw(prev.slice(0, prefixLen), resolvedSlots.slice(0, prefixLen));\n const afterRaw = extractRaw(prev.slice(removedEnd), resolvedSlots.slice(removedEnd));\n const reformatted = applyMaskToRaw(\n beforeRaw + insertedText + afterRaw,\n resolvedSlots,\n slotChar,\n transform\n );\n const maskedPrefix = applyMaskToRaw(\n beforeRaw + insertedText,\n resolvedSlots,\n slotChar,\n transform\n );\n\n if (reformatted !== prev) {\n pushUndoState();\n }\n updateValue(reformatted, maskedPrefix.length);\n },\n [pushUndoState, 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 displayValueRef.current = 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 displayValueRef.current = '';\n rawValueRef.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 displayValueRef.current = 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 displayValueRef.current = '';\n rawValueRef.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 displayValueRef.current = 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 const modifier = e.metaKey || (e.ctrlKey && !e.altKey);\n const key = e.key.toLowerCase();\n\n if (modifier && key === 'z' && !e.shiftKey) {\n e.preventDefault();\n const prev = undoStackRef.current.pop();\n if (!prev) {\n return;\n }\n redoStackRef.current.push({\n rawValue: rawValueRef.current,\n selectionStart: input.selectionStart ?? 0,\n });\n applyHistoryState(prev);\n return;\n }\n\n if (modifier && ((key === 'z' && e.shiftKey) || (key === 'y' && !e.shiftKey))) {\n e.preventDefault();\n const next = redoStackRef.current.pop();\n if (!next) {\n return;\n }\n undoStackRef.current.push({\n rawValue: rawValueRef.current,\n selectionStart: input.selectionStart ?? 0,\n });\n applyHistoryState(next);\n return;\n }\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 pushUndoState();\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 pushUndoState();\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 pushUndoState();\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 pushUndoState();\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 pushUndoState();\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 pushUndoState();\n updateValue(newValue, newCursorPos);\n }\n },\n [applyHistoryState, pushUndoState, 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 clampedStart = Math.min(start, processed.length);\n const clampedEnd = Math.min(end, processed.length);\n const beforeRaw = extractRaw(processed.slice(0, clampedStart), slots.slice(0, clampedStart));\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 pushUndoState();\n updateValue(newValue);\n\n const maskedPrefix = applyMaskToRaw(beforeRaw + pastedText, slots, slotChar, transform);\n const pasteEndPos = Math.min(maskedPrefix.length, slots.length);\n if (input === document.activeElement) {\n input.setSelectionRange(pasteEndPos, pasteEndPos);\n }\n },\n [pushUndoState, 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 const hasInitialValue = initializeInputValue(node);\n\n if (options.alwaysShowMask && !hasInitialValue) {\n const { slots, slotChar } = getResolvedOptions(options, '');\n const display = buildDisplayValue('', slots, slotChar, true);\n node.value = display;\n displayValueRef.current = display;\n setMaskedValue(display);\n }\n }\n },\n [\n handleInput,\n handleFocus,\n handleBlur,\n handleMouseDown,\n handleMouseUp,\n handleKeyDown,\n handlePaste,\n initializeInputValue,\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 displayValueRef.current = '';\n rawValueRef.current = '';\n undoStackRef.current = [];\n redoStackRef.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 displayValueRef.current = 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;AACP;AAmFA,MAAM,mBAAmB;AAEzB,SAAS,UACP,MACA,QACY;CACZ,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO,KAAK,KAAK,SAAS;EACxB,IAAI,gBAAgB,QAClB,OAAO;GAAE,MAAM;GAAS,MAAM;GAAK,SAAS;EAAK;EAEnD,OAAO;GAAE,MAAM;GAAW,MAAM;EAAK;CACvC,CAAC;CAGH,MAAM,QAAoB,CAAC;CAC3B,IAAI,WAAW;CAEf,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,OAAO,KAAK;EAElB,IAAI,SAAS,QAAQ,IAAI,IAAI,KAAK,QAAQ;GACxC;GACA,MAAM,KAAK;IAAE,MAAM;IAAW,MAAM,KAAK;GAAG,CAAC;GAC7C;EACF;EAEA,IAAI,SAAS,KAAK;GAChB,WAAW;GACX;EACF;EAEA,IAAI,OAAO,OACT,MAAM,KAAK;GAAE,MAAM;GAAS;GAAM,SAAS,OAAO;GAAO;EAAS,CAAC;OAEnE,MAAM,KAAK;GAAE,MAAM;GAAW;GAAM;EAAS,CAAC;CAElD;CAEA,OAAO;AACT;AAEA,SAAS,YAAY,gBAA2C,OAAuB;CACrF,IAAI,mBAAmB,QAAQ,mBAAmB,MAAM,mBAAmB,KAAA,GACzE,OAAO;CAET,IAAI,eAAe,SAAS,GAC1B,OAAO,eAAe,UAAU;CAElC,OAAO;AACT;AAEA,SAAS,eACP,KACA,OACA,iBACA,WACQ;CACR,IAAI,SAAS;CACb,IAAI,WAAW;CACf,IAAI,YAAY;CAEhB,KAAK,YAAY,GAAG,YAAY,MAAM,QAAQ,aAAa;EACzD,MAAM,OAAO,MAAM;EACnB,IAAI,KAAK,SAAS,WAChB,UAAU,KAAK;OACV,IAAI,WAAW,IAAI,QAAQ;GAChC,MAAM,KAAK,YAAY,UAAU,IAAI,SAAS,IAAI,IAAI;GACtD,IAAI,KAAK,WAAW,KAAK,QAAQ,KAAK,EAAE,GAAG;IACzC,UAAU;IACV;GACF,OAAO;IACL;IACA;GACF;EACF,OACE;CAEJ;CAEA,OAAO;AACT;AAEA,SAAS,kBACP,OACA,OACA,gBACA,WACQ;CACR,IAAI,CAAC,WACH,OAAO;CAGT,IAAI,UAAU;CAEd,KAAK,IAAI,IAAI,MAAM,QAAQ,IAAI,MAAM,QAAQ,KAAK;EAChD,MAAM,OAAO,MAAM;EACnB,IAAI,KAAK,SAAS,WAChB,WAAW,KAAK;OACX;GACL,MAAM,KAAK,YAAY,gBAAgB,CAAC;GACxC,IAAI,CAAC,IACH;GAEF,WAAW;EACb;CACF;CAEA,OAAO;AACT;AAEA,SAAS,WAAW,QAAgB,OAA2B;CAC7D,IAAI,MAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,UAAU,IAAI,MAAM,QAAQ,KACrD,IAAI,MAAM,EAAE,CAAC,SAAS,SACpB,OAAO,OAAO;CAGlB,OAAO;AACT;AAEA,SAAS,cAAc,QAAgB,OAA4B;CACjE,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAChC,IAAI,MAAM,EAAE,CAAC,SAAS,WAAW,CAAC,MAAM,EAAE,CAAC,UAAU;EACnD,IAAI,KAAK,OAAO,QACd,OAAO;EAET,IAAI,CAAC,MAAM,EAAE,CAAC,QAAS,KAAK,OAAO,EAAE,GACnC,OAAO;CAEX;CAEF,OAAO;AACT;AAEA,SAAS,mBAAmB,OAAmB,MAAsB;CACnE,KAAK,IAAI,IAAI,MAAM,IAAI,MAAM,QAAQ,KACnC,IAAI,MAAM,EAAE,CAAC,SAAS,SACpB,OAAO;CAGX,OAAO,MAAM;AACf;AAEA,SAAS,mBAAmB,OAAmB,MAAsB;CACnE,KAAK,IAAI,IAAI,MAAM,KAAK,GAAG,KACzB,IAAI,MAAM,EAAE,CAAC,SAAS,SACpB,OAAO;CAGX,OAAO;AACT;AAEA,SAAS,aACP,YACA,OACA,iBACQ;CACR,IAAI,SAAS;CACb,IAAI,aAAa;CAEjB,KACE,IAAI,YAAY,GAChB,YAAY,MAAM,UAAU,cAAc,WAAW,QACrD,aACA;EACA,MAAM,OAAO,MAAM;EAEnB,IAAI,KAAK,SAAS,WAAW;GAC3B,UAAU,KAAK;GACf,IAAI,aAAa,WAAW,UAAU,WAAW,gBAAgB,KAAK,MACpE;GAEF;EACF;EAEA,IAAI,cAAc,WAAW,QAC3B;EAGF,OAAO,aAAa,WAAW,QAAQ;GACrC,MAAM,KAAK,WAAW;GACtB;GAEA,IAAI,KAAK,QAAS,KAAK,EAAE,GAAG;IAC1B,UAAU;IACV;GACF;EACF;EAEA,IAAI,OAAO,UAAU,WACnB;CAEJ;CAEA,OAAO;AACT;AAEA,SAAS,mBAAmB,SAAyB,UAAkB;CACrE,MAAM,SAAS;EAAE,GAAG;EAAgB,GAAG,QAAQ;CAAO;CACtD,IAAI,OAAO,QAAQ;CACnB,IAAI,WAAsC,QAAQ,aAAa,KAAA,IAAY,MAAM,QAAQ;CACzF,IAAI,WAAW,QAAQ,YAAY;CAEnC,IAAI,QAAQ,QAAQ;EAClB,MAAM,YAAY,QAAQ,OAAO,QAAQ;EACzC,IAAI,WAAW;GACb,IAAI,UAAU,SAAS,KAAA,GACrB,OAAO,UAAU;GAEnB,IAAI,UAAU,WAAW,KAAA,GACvB,OAAO,OAAO,QAAQ,UAAU,MAAM;GAExC,IAAI,UAAU,aAAa,KAAA,GACzB,WAAW,UAAU;GAEvB,IAAI,UAAU,aAAa,KAAA,GACzB,WAAW,UAAU;EAEzB;CACF;CAGA,OAAO;EAAE,OADK,UAAU,MAAM,MACjB;EAAG;EAAU;EAAU;EAAQ,WAAW,QAAQ;CAAU;AAC3E;AAEA,SAAgB,WAAW,KAAa,SAAiC;CACvE,MAAM,EAAE,OAAO,UAAU,cAAc,mBAAmB,SAAS,GAAG;CACtE,OAAO,eAAe,KAAK,OAAO,UAAU,SAAS;AACvD;AAEA,SAAgB,aAAa,QAAgB,SAAiC;CAC5E,MAAM,EAAE,UAAU,mBAAmB,SAAS,EAAE;CAChD,OAAO,WAAW,QAAQ,KAAK;AACjC;AAEA,SAAgB,eAAe,QAAgB,SAAkC;CAC/E,MAAM,EAAE,UAAU,mBAAmB,SAAS,EAAE;CAChD,OAAO,cAAc,QAAQ,KAAK;AACpC;AAEA,SAAgB,gBAAgB,MAA+B,SAAiC;CAC9F,MAAM,EAAE,UAAU,mBAAmB,SAAS,EAAE;CAChD,IAAI,UAAU;CAEd,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,SAAS,WAChB,WAAW,KAAK,KAAK,QAAQ,uBAAuB,MAAM;MACrD;EACL,MAAM,MAAM,KAAK,QAAS;EAC1B,IAAI,SAAS,gBACX,WAAW,KAAK,WAAW,GAAG,IAAI,KAAK;OAEvC,WAAW,KAAK,WAAW,IAAI,IAAI,MAAM,IAAI,IAAI;CAErD;CAGF,OAAO;AACT;AAEA,SAAgB,QAAQ,SAA6C;CACnE,MAAM,cAAA,GAAaA,MAAAA,OAAAA,CAAO,OAAO;CACjC,WAAW,UAAU;CAErB,MAAM,YAAA,GAAWA,MAAAA,OAAAA,CAAgC,IAAI;CACrD,MAAM,CAAC,aAAa,mBAAA,GAAkBC,MAAAA,SAAAA,CAAS,EAAE;CACjD,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAS,EAAE;CAC3C,MAAM,gBAAA,GAAeD,MAAAA,OAAAA,CAAO,EAAE;CAC9B,MAAM,mBAAA,GAAkBA,MAAAA,OAAAA,CAAO,EAAE;CACjC,MAAM,eAAA,GAAcA,MAAAA,OAAAA,CAAO,EAAE;CAC7B,MAAM,kBAAA,GAAiBA,MAAAA,OAAAA,CAAO,KAAK;CACnC,MAAM,gBAAA,GAAeA,MAAAA,OAAAA,CAAO,KAAK;CACjC,MAAM,gBAAA,GAAeA,MAAAA,OAAAA,CAAoB,CAAC,CAAC;CAC3C,MAAM,gBAAA,GAAeA,MAAAA,OAAAA,CAAoB,CAAC,CAAC;CAE3C,MAAM,cAAA,GAAaE,MAAAA,YAAAA,OAAkB;EACnC,MAAM,OAAO,WAAW;EACxB,OAAO,mBAAmB,MAAM,QAAQ;CAC1C,GAAG,CAAC,QAAQ,CAAC;CAEb,MAAM,cAAA,GAAaA,MAAAA,YAAAA,EAChB,EACC,aACA,QACA,cACA,eACA,WACA,mBAQI;EACJ,MAAM,OAAO,WAAW;EAExB,aAAa,UAAU;EACvB,gBAAgB,UAAU;EAC1B,YAAY,UAAU;EACtB,eAAe,YAAY;EAC3B,YAAY,MAAM;EAElB,IAAI,SAAS,SAAS;GACpB,SAAS,QAAQ,QAAQ;GACzB,IAAI,cAAc,KAAA,KAAa,SAAS,kBAAkB,SAAS,SAAS;IAC1E,MAAM,MAAM,KAAK,IAAI,WAAW,YAAY,MAAM;IAClD,SAAS,QAAQ,kBAAkB,KAAK,GAAG;GAC7C;EACF;EAEA,IAAI,gBAAgB,KAAK,aACvB,KAAK,YAAY,QAAQ,YAAY;EAGvC,MAAM,WAAW,cAAc,aAAa,aAAa;EACzD,IAAI,gBAAgB,YAAY,CAAC,eAAe,WAAW,KAAK,YAC9D,KAAK,WAAW,cAAc,MAAM;EAEtC,eAAe,UAAU;CAC3B,GACA,CAAC,CACH;CAEA,MAAM,eAAA,GAAcA,MAAAA,YAAAA,EACjB,WAAmB,cAAuB;EACzC,MAAM,OAAO,WAAW;EACxB,MAAM,EAAE,UAAU,mBAChB,MACA,WAAW,WAAW,mBAAmB,MAAM,EAAE,CAAC,CAAC,KAAK,CAC1D;EAGA,MAAM,EAAE,OAAO,eAAe,aAAa,mBAAmB,MAFlD,WAAW,WAAW,KAEoC,CAAC;EAEvE,MAAM,cAAc,aAAa,WAAW,eAAe,QAAQ;EACnE,MAAM,SAAS,WAAW,aAAa,aAAa;EAEpD,MAAM,YAAY,KAAK,kBAAkB,aAAa;EACtD,MAAM,cAAc,KAAK,oBAAoB;EAG7C,MAAM,eAAe,kBAAkB,aAAa,eAAe,UAF3C,cAAc,eAAe,YAAY,SAAS,EAEkB;EAE5F,WAAW;GACT;GACA;GACA;GACA;GACA;GACA,cAAc;EAChB,CAAC;EAED,OAAO;GAAE;GAAc;GAAQ;GAAa;EAAc;CAC5D,GACA,CAAC,YAAY,UAAU,CACzB;CAEA,MAAM,wBAAA,GAAuBA,MAAAA,YAAAA,EAC1B,SAA2B;EAC1B,MAAM,OAAO,WAAW;EAExB,IAAI,CAAC,KAAK,OACR,OAAO;EAGT,MAAM,aAAa,KAAK,UAAU,gBAAgB;EAClD,MAAM,SAAS,OAAmB,mBAChC,aACI,eAAe,YAAY,SAAS,OAAO,cAAc,IACzD,aAAa,KAAK,OAAO,OAAO,cAAc;EAEpD,MAAM,EAAE,OAAO,cAAc,UAAU,oBAAoB,mBAAmB,MAAM,EAAE;EAGtF,MAAM,EAAE,OAAO,eAAe,aAAa,mBAAmB,MAD3C,WADM,MAAM,cAAc,eACA,GAAG,YAC6B,CAAC;EAC9E,MAAM,cAAc,MAAM,eAAe,QAAQ;EACjD,MAAM,SAAS,WAAW,aAAa,aAAa;EACpD,MAAM,YAAY,KAAK,kBAAkB,aAAa;EACtD,MAAM,cAAc,KAAK,oBAAoB;EAE7C,MAAM,eAAe,kBAAkB,aAAa,eAAe,UAD3C,cAAc,eAAe,YAAY,SAAS,EACkB;EAE5F,WAAW;GACT;GACA;GACA;GACA;GACA,cAAc;EAChB,CAAC;EAED,OAAO;CACT,GACA,CAAC,UAAU,CACb;CAEA,MAAM,iBAAA,GAAgBA,MAAAA,YAAAA,OAAkB;EAEtC,MAAM,iBADQ,SAAS,SACO,kBAAkB,YAAY,QAAQ;EACpE,MAAM,QAAmB;GACvB,UAAU,YAAY;GACtB;EACF;EACA,MAAM,QAAQ,aAAa;EAC3B,MAAM,MAAM,MAAM,MAAM,SAAS;EACjC,IAAI,OAAO,IAAI,aAAa,MAAM,YAAY,IAAI,mBAAmB,MAAM,gBACzE;EAEF,MAAM,KAAK,KAAK;EAChB,IAAI,MAAM,SAAS,kBACjB,MAAM,MAAM;EAEd,aAAa,UAAU,CAAC;CAC1B,GAAG,CAAC,CAAC;CAEL,MAAM,qBAAA,GAAoBA,MAAAA,YAAAA,EACvB,WAAsB;EACrB,MAAM,OAAO,WAAW;EACxB,MAAM,EAAE,OAAO,UAAU,cAAc,mBAAmB,MAAM,OAAO,QAAQ;EAC/E,MAAM,YAAY,eAAe,OAAO,UAAU,OAAO,UAAU,SAAS;EAC5E,YAAY,WAAW,OAAO,cAAc;CAC9C,GACA,CAAC,WAAW,CACd;CAEA,MAAM,eAAA,GAAcA,MAAAA,YAAAA,EACjB,MAAa;EACZ,MAAM,QAAQ,EAAE;EAChB,MAAM,OAAO,WAAW;EAExB,MAAM,EAAE,OAAO,eAAe,UAAU,cAAc,mBAAmB,MAAM,EAAE;EACjF,MAAM,OAAO,gBAAgB;EAC7B,MAAM,OAAO,MAAM;EAEnB,IAAI,YAAY;EAChB,MAAM,YAAY,KAAK,IAAI,KAAK,QAAQ,KAAK,MAAM;EACnD,OAAO,YAAY,aAAa,KAAK,eAAe,KAAK,YACvD;EAGF,IAAI,YAAY;EAChB,MAAM,YAAY,KAAK,IAAI,KAAK,SAAS,WAAW,KAAK,SAAS,SAAS;EAC3E,OACE,YAAY,aACZ,KAAK,KAAK,SAAS,IAAI,eAAe,KAAK,KAAK,SAAS,IAAI,YAE7D;EAGF,MAAM,eAAe,KAAK,MAAM,WAAW,KAAK,SAAS,SAAS;EAClE,MAAM,aAAa,KAAK,SAAS;EAEjC,MAAM,YAAY,WAAW,KAAK,MAAM,GAAG,SAAS,GAAG,cAAc,MAAM,GAAG,SAAS,CAAC;EACxF,MAAM,WAAW,WAAW,KAAK,MAAM,UAAU,GAAG,cAAc,MAAM,UAAU,CAAC;EACnF,MAAM,cAAc,eAClB,YAAY,eAAe,UAC3B,eACA,UACA,SACF;EACA,MAAM,eAAe,eACnB,YAAY,cACZ,eACA,UACA,SACF;EAEA,IAAI,gBAAgB,MAClB,cAAc;EAEhB,YAAY,aAAa,aAAa,MAAM;CAC9C,GACA,CAAC,eAAe,WAAW,CAC7B;CAEA,MAAM,0BAAA,GAAyBA,MAAAA,YAAAA,EAAa,UAA4B;EACtE,MAAM,QAAQ,MAAM,kBAAkB;EAEtC,IAAI,WADQ,MAAM,gBAAgB,IAEhC;EAGF,MAAM,OAAO,WAAW;EACxB,MAAM,EAAE,UAAU,mBAAmB,MAAM,EAAE;EAC7C,MAAM,YAAY,aAAa;EAC/B,MAAM,SACJ,UAAU,SAAS,IACf,yBAAyB,UAAU,QAAQ,OAAO,SAAS,IAC3D,mBAAmB,OAAO,CAAC;EACjC,MAAM,WAAW,mBAAmB,OAAO,CAAC;EAE5C,IAAI,QAAQ,UAAU,QAAQ,UAC5B,MAAM,kBAAkB,QAAQ,MAAM;CAE1C,GAAG,CAAC,CAAC;CAEL,MAAM,eAAA,GAAcA,MAAAA,YAAAA,OAAkB;EACpC,aAAa,UAAU;EACvB,MAAM,OAAO,WAAW;EACxB,MAAM,QAAQ,SAAS;EAEvB,IAAI,CAAC,OACH;EAGF,MAAM,EAAE,OAAO,aAAa,mBAAmB,MAAM,EAAE;EACvD,MAAM,cAAc,KAAK,oBAAoB;EAC7C,MAAM,YAAY,aAAa;EAE/B,IAAI,eAAe,KAAK,gBAAgB;GACtC,MAAM,UAAU,kBAAkB,WAAW,OAAO,UAAU,IAAI;GAClE,MAAM,QAAQ;GACd,gBAAgB,UAAU;GAC1B,eAAe,OAAO;EACxB;EAEA,4BAA4B;GAC1B,IAAI,UAAU,SAAS,eACrB,uBAAuB,KAAK;EAEhC,CAAC;CACH,GAAG,CAAC,sBAAsB,CAAC;CAE3B,MAAM,iBAAA,GAAgBA,MAAAA,YAAAA,OAAkB;EACtC,MAAM,QAAQ,SAAS;EACvB,IAAI,CAAC,SAAS,UAAU,SAAS,eAC/B;EAGF,uBAAuB,KAAK;CAC9B,GAAG,CAAC,sBAAsB,CAAC;CAE3B,MAAM,mBAAA,GAAkBA,MAAAA,YAAAA,OAAkB;EACxC,MAAM,QAAQ,SAAS;EACvB,IAAI,CAAC,OACH;EAGF,4BAA4B;GAC1B,IAAI,UAAU,SAAS,eACrB;GAGF,MAAM,QAAQ,MAAM,kBAAkB;GAEtC,IAAI,WADQ,MAAM,gBAAgB,IAEhC;GAGF,MAAM,OAAO,WAAW;GACxB,MAAM,EAAE,UAAU,mBAAmB,MAAM,EAAE;GAC7C,MAAM,YAAY,aAAa;GAC/B,MAAM,SACJ,UAAU,SAAS,IACf,yBAAyB,UAAU,QAAQ,OAAO,SAAS,IAC3D,mBAAmB,OAAO,CAAC;GAEjC,IAAI,QAAQ,QACV,MAAM,kBAAkB,QAAQ,MAAM;EAE1C,CAAC;CACH,GAAG,CAAC,CAAC;CAEL,MAAM,cAAA,GAAaA,MAAAA,YAAAA,OAAkB;EACnC,aAAa,UAAU;EACvB,MAAM,OAAO,WAAW;EACxB,MAAM,QAAQ,SAAS;EAEvB,IAAI,CAAC,OACH;EAGF,MAAM,EAAE,OAAO,aAAa,mBAAmB,MAAM,QAAQ;EAC7D,MAAM,uBAAuB,kBAAkB,aAAa,SAAS,OAAO,UAAU,IAAI;EAC1F,MAAM,YACJ,MAAM,UAAU,uBACZ,aAAa,UACb,aAAa,MAAM,OAAO,OAAO,QAAQ;EAC/C,MAAM,WAAW,cAAc,WAAW,KAAK;EAE/C,IAAI,KAAK,aAAa,CAAC,YAAY,UAAU,SAAS,GAAG;GACvD,MAAM,QAAQ;GACd,aAAa,UAAU;GACvB,gBAAgB,UAAU;GAC1B,YAAY,UAAU;GACtB,eAAe,EAAE;GACjB,YAAY,EAAE;GACd,eAAe,UAAU;GAEzB,IAAI,KAAK,aACP,KAAK,YAAY,IAAI,EAAE;GAGzB,IAAI,KAAK,gBAAgB;IACvB,MAAM,eAAe,kBAAkB,IAAI,OAAO,UAAU,IAAI;IAChE,MAAM,QAAQ;IACd,gBAAgB,UAAU;IAC1B,eAAe,YAAY;GAC7B;GACA;EACF;EAEA,IAAI,CAAC,KAAK,kBAAkB,CAAC,UAAU;GACrC,IAAI,WAAW,WAAW,KAAK,CAAC,CAAC,WAAW,GAAG;IAC7C,MAAM,QAAQ;IACd,aAAa,UAAU;IACvB,gBAAgB,UAAU;IAC1B,YAAY,UAAU;IACtB,eAAe,EAAE;IACjB,YAAY,EAAE;IACd,eAAe,UAAU;IAEzB,IAAI,KAAK,aACP,KAAK,YAAY,IAAI,EAAE;IAEzB;GACF;GAEA,MAAM,UAAU,kBAAkB,WAAW,OAAO,UAAU,KAAK;GACnE,MAAM,QAAQ;GACd,gBAAgB,UAAU;GAC1B,eAAe,OAAO;EACxB;CACF,GAAG,CAAC,QAAQ,CAAC;CAEb,MAAM,iBAAA,GAAgBA,MAAAA,YAAAA,EACnB,MAAqB;EACpB,MAAM,QAAQ,EAAE;EAChB,MAAM,OAAO,WAAW;EAExB,MAAM,EAAE,OAAO,UAAU,cAAc,mBAAmB,MAAM,QAAQ;EACxE,MAAM,QAAQ,MAAM,kBAAkB;EACtC,MAAM,MAAM,MAAM,gBAAgB;EAClC,MAAM,YAAY,aAAa;EAE/B,MAAM,WAAW,EAAE,WAAY,EAAE,WAAW,CAAC,EAAE;EAC/C,MAAM,MAAM,EAAE,IAAI,YAAY;EAE9B,IAAI,YAAY,QAAQ,OAAO,CAAC,EAAE,UAAU;GAC1C,EAAE,eAAe;GACjB,MAAM,OAAO,aAAa,QAAQ,IAAI;GACtC,IAAI,CAAC,MACH;GAEF,aAAa,QAAQ,KAAK;IACxB,UAAU,YAAY;IACtB,gBAAgB,MAAM,kBAAkB;GAC1C,CAAC;GACD,kBAAkB,IAAI;GACtB;EACF;EAEA,IAAI,aAAc,QAAQ,OAAO,EAAE,YAAc,QAAQ,OAAO,CAAC,EAAE,WAAY;GAC7E,EAAE,eAAe;GACjB,MAAM,OAAO,aAAa,QAAQ,IAAI;GACtC,IAAI,CAAC,MACH;GAEF,aAAa,QAAQ,KAAK;IACxB,UAAU,YAAY;IACtB,gBAAgB,MAAM,kBAAkB;GAC1C,CAAC;GACD,kBAAkB,IAAI;GACtB;EACF;EAEA,IAAI,EAAE,QAAQ,aAAa;GACzB,EAAE,eAAe;GAEjB,IAAI,EAAE,WAAY,EAAE,WAAW,CAAC,EAAE,QAAS;IACzC,MAAM,eAAe,KAAK,IAAI,OAAO,UAAU,MAAM;IAErD,MAAM,WAAW,eADA,WAAW,UAAU,MAAM,YAAY,GAAG,MAAM,MAAM,YAAY,CAC5C,GAAG,OAAO,UAAU,SAAS;IACpE,cAAc;IACd,YAAY,UAAU,CAAC;IACvB;GACF;GAEA,IAAI,UAAU,KAAK;IACjB,MAAM,aAAa,KAAK,IAAI,KAAK,UAAU,MAAM;IACjD,MAAM,SAAS,UAAU,MAAM,GAAG,KAAK;IACvC,MAAM,WAAW,WAAW,UAAU,MAAM,UAAU,GAAG,MAAM,MAAM,UAAU,CAAC;IAChF,MAAM,WAAW,eACf,WAAW,QAAQ,KAAK,IAAI,UAC5B,OACA,UACA,SACF;IACA,cAAc;IACd,YAAY,UAAU,KAAK;IAC3B;GACF;GAEA,IAAI,UAAU,GACZ;GAGF,IAAI,YAAY,QAAQ;GACxB,OAAO,aAAa,KAAK,MAAM,cAAc,MAAM,UAAU,CAAC,SAAS,WACrE;GAGF,IAAI,YAAY,GACd;GAKF,MAAM,WAAW,eAFC,WAAW,UAAU,MAAM,GAAG,SAAS,GAAG,MAAM,MAAM,GAAG,SAAS,CAE5C,IADvB,WAAW,UAAU,MAAM,YAAY,CAAC,GAAG,MAAM,MAAM,YAAY,CAAC,CAClC,GAAG,OAAO,UAAU,SAAS;GAChF,cAAc;GACd,YAAY,UAAU,SAAS;EACjC,OAAO,IAAI,EAAE,QAAQ,UAAU;GAC7B,EAAE,eAAe;GAEjB,IAAI,UAAU,KAAK;IACjB,MAAM,aAAa,KAAK,IAAI,KAAK,UAAU,MAAM;IACjD,MAAM,SAAS,UAAU,MAAM,GAAG,KAAK;IACvC,MAAM,WAAW,WAAW,UAAU,MAAM,UAAU,GAAG,MAAM,MAAM,UAAU,CAAC;IAChF,MAAM,WAAW,eACf,WAAW,QAAQ,KAAK,IAAI,UAC5B,OACA,UACA,SACF;IACA,cAAc;IACd,YAAY,UAAU,KAAK;IAC3B;GACF;GAEA,IAAI,YAAY;GAChB,OACE,YAAY,MAAM,UAClB,MAAM,cACN,MAAM,UAAU,CAAC,SAAS,WAE1B;GAGF,IAAI,aAAa,UAAU,QACzB;GAKF,MAAM,WAAW,eAFC,WAAW,UAAU,MAAM,GAAG,KAAK,GAAG,MAAM,MAAM,GAAG,KAAK,CAEpC,IADvB,WAAW,UAAU,MAAM,YAAY,CAAC,GAAG,MAAM,MAAM,YAAY,CAAC,CAClC,GAAG,OAAO,UAAU,SAAS;GAChF,cAAc;GACd,YAAY,UAAU,KAAK;EAC7B,OAAO,IAAI,EAAE,QAAQ,gBAAgB,CAAC,EAAE,UAAU;GAChD,MAAM,UAAU,yBAAyB,QAAQ,GAAG,OAAO,MAAM,KAAK;GACtE,IAAI,YAAY,QAAQ,GAAG;IACzB,EAAE,eAAe;IACjB,MAAM,kBAAkB,SAAS,OAAO;GAC1C;EACF,OAAO,IAAI,EAAE,QAAQ,eAAe,CAAC,EAAE,UACjC;OAAA,QAAQ,GAAG;IACb,MAAM,YAAY,mBAAmB,OAAO,QAAQ,CAAC;IACrD,IAAI,aAAa,KAAK,cAAc,QAAQ,GAAG;KAC7C,EAAE,eAAe;KACjB,MAAM,kBAAkB,YAAY,GAAG,YAAY,CAAC;IACtD;GACF;SACK,IAAI,EAAE,IAAI,WAAW,KAAK,CAAC,EAAE,WAAW,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ;GACtE,EAAE,eAAe;GAEjB,IAAI,YAAY,KAAK,IAAI,OAAO,UAAU,MAAM;GAChD,OACE,YAAY,MAAM,UAClB,MAAM,cACN,MAAM,UAAU,CAAC,SAAS,WAE1B;GAGF,IAAI,aAAa,MAAM,QACrB;GAGF,MAAM,OAAO,MAAM;GACnB,MAAM,KAAK,YAAY,UAAU,EAAE,GAAG,IAAI,EAAE;GAC5C,IAAI,CAAC,KAAK,QAAS,KAAK,EAAE,GACxB;GAGF,MAAM,YAAY,WAAW,UAAU,MAAM,GAAG,SAAS,GAAG,MAAM,MAAM,GAAG,SAAS,CAAC;GACrF,MAAM,WACJ,QAAQ,MACJ,WACE,UAAU,MAAM,KAAK,IAAI,KAAK,UAAU,MAAM,CAAC,GAC/C,MAAM,MAAM,KAAK,IAAI,KAAK,UAAU,MAAM,CAAC,CAC7C,IACA,WAAW,UAAU,MAAM,SAAS,GAAG,MAAM,MAAM,SAAS,CAAC;GACnE,MAAM,WAAW,eAAe,YAAY,KAAK,UAAU,OAAO,UAAU,SAAS;GACrF,MAAM,eAAe,yBAAyB,YAAY,GAAG,OAAO,QAAQ;GAC5E,cAAc;GACd,YAAY,UAAU,YAAY;EACpC;CACF,GACA;EAAC;EAAmB;EAAe;EAAU;CAAW,CAC1D;CAEA,MAAM,eAAA,GAAcA,MAAAA,YAAAA,EACjB,MAAsB;EACrB,EAAE,eAAe;EACjB,MAAM,QAAQ,EAAE;EAChB,MAAM,OAAO,WAAW;EAExB,MAAM,aAAa,EAAE,eAAe,QAAQ,MAAM,KAAK;EACvD,MAAM,QAAQ,MAAM,kBAAkB;EACtC,MAAM,MAAM,MAAM,gBAAgB;EAClC,MAAM,YAAY,aAAa;EAE/B,MAAM,EAAE,OAAO,UAAU,cAAc,mBAAmB,MAAM,EAAE;EAClE,MAAM,eAAe,KAAK,IAAI,OAAO,UAAU,MAAM;EACrD,MAAM,aAAa,KAAK,IAAI,KAAK,UAAU,MAAM;EACjD,MAAM,YAAY,WAAW,UAAU,MAAM,GAAG,YAAY,GAAG,MAAM,MAAM,GAAG,YAAY,CAAC;EAC3F,MAAM,WAAW,WAAW,UAAU,MAAM,UAAU,GAAG,MAAM,MAAM,UAAU,CAAC;EAChF,MAAM,WAAW,eACf,YAAY,aAAa,UACzB,OACA,UACA,SACF;EAEA,cAAc;EACd,YAAY,QAAQ;EAEpB,MAAM,eAAe,eAAe,YAAY,YAAY,OAAO,UAAU,SAAS;EACtF,MAAM,cAAc,KAAK,IAAI,aAAa,QAAQ,MAAM,MAAM;EAC9D,IAAI,UAAU,SAAS,eACrB,MAAM,kBAAkB,aAAa,WAAW;CAEpD,GACA,CAAC,eAAe,WAAW,CAC7B;CAEA,MAAM,qBAAA,GAAoBA,MAAAA,YAAAA,EAAa,UAA4B;EAGjE,IAFa,WAAW,QAEf,SACP,MAAM,aAAa,gBAAgB,MAAM;OAEzC,MAAM,gBAAgB,cAAc;CAExC,GAAG,CAAC,CAAC;CAEL,MAAM,eAAA,GAAcA,MAAAA,YAAAA,EACjB,SAAkC;EACjC,MAAM,YAAY,SAAS;EAE3B,IAAI,WAAW;GACb,UAAU,oBAAoB,SAAS,WAAW;GAClD,UAAU,oBAAoB,SAAS,WAAW;GAClD,UAAU,oBAAoB,QAAQ,UAAU;GAChD,UAAU,oBAAoB,aAAa,eAAe;GAC1D,UAAU,oBAAoB,WAAW,aAAa;GACtD,UAAU,oBAAoB,WAAW,aAA8B;GACvE,UAAU,oBAAoB,SAAS,WAA4B;EACrE;EAEA,SAAS,UAAU;EAEnB,IAAI,MAAM;GACR,KAAK,iBAAiB,SAAS,WAAW;GAC1C,KAAK,iBAAiB,SAAS,WAAW;GAC1C,KAAK,iBAAiB,QAAQ,UAAU;GACxC,KAAK,iBAAiB,aAAa,eAAe;GAClD,KAAK,iBAAiB,WAAW,aAAa;GAC9C,KAAK,iBAAiB,WAAW,aAA8B;GAC/D,KAAK,iBAAiB,SAAS,WAA4B;GAE3D,kBAAkB,IAAI;GAEtB,MAAM,kBAAkB,qBAAqB,IAAI;GAEjD,IAAI,QAAQ,kBAAkB,CAAC,iBAAiB;IAC9C,MAAM,EAAE,OAAO,aAAa,mBAAmB,SAAS,EAAE;IAC1D,MAAM,UAAU,kBAAkB,IAAI,OAAO,UAAU,IAAI;IAC3D,KAAK,QAAQ;IACb,gBAAgB,UAAU;IAC1B,eAAe,OAAO;GACxB;EACF;CACF,GACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF;CAEA,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,MAAM,QAAQ,SAAS;EACvB,IAAI,CAAC,OACH;EAGF,kBAAkB,KAAK;CACzB,GAAG,CAAC,QAAQ,SAAS,iBAAiB,CAAC;CAqCvC,OAAO;EACL,KAAK;EACL,OAAO;EACP;EACA,mBAvCwB;GACxB,MAAM,EAAE,UAAU,WAAW;GAC7B,OAAO,cAAc,aAAa,SAAS,KAAK;EAClD,EAAA,CAoCW;EACT,QAAA,GAnCYA,MAAAA,YAAAA,OAAkB;GAC9B,MAAM,OAAO,WAAW;GACxB,MAAM,QAAQ,SAAS;GAEvB,aAAa,UAAU;GACvB,gBAAgB,UAAU;GAC1B,YAAY,UAAU;GACtB,aAAa,UAAU,CAAC;GACxB,aAAa,UAAU,CAAC;GACxB,eAAe,EAAE;GACjB,YAAY,EAAE;GACd,eAAe,UAAU;GAEzB,IAAI,OAAO;IACT,IAAI,KAAK,gBAAgB;KACvB,MAAM,EAAE,OAAO,aAAa,mBAAmB,MAAM,EAAE;KACvD,MAAM,UAAU,kBAAkB,IAAI,OAAO,UAAU,IAAI;KAC3D,MAAM,QAAQ;KACd,gBAAgB,UAAU;KAC1B,eAAe,OAAO;IACxB,OACE,MAAM,QAAQ;GAElB;GAEA,IAAI,KAAK,aACP,KAAK,YAAY,IAAI,EAAE;EAE3B,GAAG,CAAC,CAOE;CACN;AACF;AAEA,SAAS,yBAAyB,MAAc,OAAmB,OAAuB;CACxF,IAAI,MAAM;CACV,OAAO,MAAM,MAAM,UAAU,MAAM,MAAM,UAAU,MAAM,QAAQ,MAAM,IAAI,CAAC,SAAS,WACnF;CAEF,OAAO;AACT"}
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
const require_random_id = require("../utils/random-id/random-id.cjs");
|
|
3
3
|
let react = require("react");
|
|
4
4
|
//#region packages/@mantine/hooks/src/use-scroll-spy/use-scroll-spy.ts
|
|
5
|
-
function
|
|
5
|
+
function resolveHeadingNode(heading, selector, index) {
|
|
6
|
+
if (heading.isConnected) return heading;
|
|
7
|
+
const nodeById = heading.id ? document.getElementById(heading.id) : null;
|
|
8
|
+
if (nodeById) return nodeById;
|
|
9
|
+
return document.querySelectorAll(selector)[index] || heading;
|
|
10
|
+
}
|
|
11
|
+
function getHeadingsData(headings, getDepth, getValue, selector) {
|
|
6
12
|
const result = [];
|
|
7
13
|
for (let i = 0; i < headings.length; i += 1) {
|
|
8
14
|
const heading = headings[i];
|
|
@@ -10,7 +16,7 @@ function getHeadingsData(headings, getDepth, getValue) {
|
|
|
10
16
|
depth: getDepth(heading),
|
|
11
17
|
value: getValue(heading),
|
|
12
18
|
id: heading.id || require_random_id.randomId(),
|
|
13
|
-
getNode: () => heading
|
|
19
|
+
getNode: () => resolveHeadingNode(heading, selector, i)
|
|
14
20
|
});
|
|
15
21
|
}
|
|
16
22
|
return result;
|
|
@@ -48,7 +54,7 @@ function useScrollSpy({ selector = "h1, h2, h3, h4, h5, h6", getDepth = getDefau
|
|
|
48
54
|
setActive(getActiveElement(headingsRef.current.map((d) => d.getNode().getBoundingClientRect()), offset));
|
|
49
55
|
});
|
|
50
56
|
const initialize = () => {
|
|
51
|
-
const headings = getHeadingsData(Array.from(document.querySelectorAll(selector)), getDepth, getValue);
|
|
57
|
+
const headings = getHeadingsData(Array.from(document.querySelectorAll(selector)), getDepth, getValue, selector);
|
|
52
58
|
headingsRef.current = headings;
|
|
53
59
|
setInitialized(true);
|
|
54
60
|
setData(headings);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-scroll-spy.cjs","names":["randomId","useState","useRef","useEffectEvent"],"sources":["../../src/use-scroll-spy/use-scroll-spy.ts"],"sourcesContent":["import React, { useEffect, useEffectEvent, useRef, useState } from 'react';\nimport { randomId } from '../utils';\n\nfunction getHeadingsData(\n headings: HTMLElement[],\n getDepth: (element: HTMLElement) => number,\n getValue: (element: HTMLElement) => string\n): UseScrollSpyHeadingData[] {\n const result: UseScrollSpyHeadingData[] = [];\n\n for (let i = 0; i < headings.length; i += 1) {\n const heading = headings[i];\n result.push({\n depth: getDepth(heading),\n value: getValue(heading),\n id: heading.id || randomId(),\n getNode: () => (heading
|
|
1
|
+
{"version":3,"file":"use-scroll-spy.cjs","names":["randomId","useState","useRef","useEffectEvent"],"sources":["../../src/use-scroll-spy/use-scroll-spy.ts"],"sourcesContent":["import React, { useEffect, useEffectEvent, useRef, useState } from 'react';\nimport { randomId } from '../utils';\n\nfunction resolveHeadingNode(heading: HTMLElement, selector: string, index: number): HTMLElement {\n if (heading.isConnected) {\n return heading;\n }\n\n const nodeById = heading.id ? document.getElementById(heading.id) : null;\n\n if (nodeById) {\n return nodeById;\n }\n\n return document.querySelectorAll<HTMLElement>(selector)[index] || heading;\n}\n\nfunction getHeadingsData(\n headings: HTMLElement[],\n getDepth: (element: HTMLElement) => number,\n getValue: (element: HTMLElement) => string,\n selector: string\n): UseScrollSpyHeadingData[] {\n const result: UseScrollSpyHeadingData[] = [];\n\n for (let i = 0; i < headings.length; i += 1) {\n const heading = headings[i];\n result.push({\n depth: getDepth(heading),\n value: getValue(heading),\n id: heading.id || randomId(),\n getNode: () => resolveHeadingNode(heading, selector, i),\n });\n }\n\n return result;\n}\n\nfunction getActiveElement(rects: DOMRect[], offset: number = 0) {\n if (rects.length === 0) {\n return -1;\n }\n\n const closest = rects.reduce(\n (acc, item, index) => {\n if (Math.abs(acc.position - offset) < Math.abs(item.y - offset)) {\n return acc;\n }\n\n return {\n index,\n position: item.y,\n };\n },\n { index: 0, position: rects[0].y }\n );\n\n return closest.index;\n}\n\nfunction getDefaultDepth(element: HTMLElement) {\n return Number(element.tagName[1]);\n}\n\nfunction getDefaultValue(element: HTMLElement) {\n return element.textContent || '';\n}\n\nfunction resolveScrollHost(\n scrollHost: HTMLElement | React.RefObject<HTMLElement | null> | undefined\n): HTMLElement | Window {\n if (!scrollHost) {\n return window;\n }\n\n if ('current' in scrollHost) {\n return scrollHost.current || window;\n }\n\n return scrollHost;\n}\n\nexport interface UseScrollSpyHeadingData {\n /** Heading depth, 1-6 */\n depth: number;\n\n /** Heading text content value */\n value: string;\n\n /** Heading id */\n id: string;\n\n /** Function to get heading node */\n getNode: () => HTMLElement;\n}\n\nexport interface UseScrollSpyOptions {\n /** Selector to get headings, `'h1, h2, h3, h4, h5, h6'` by default */\n selector?: string;\n\n /** A function to retrieve depth of heading, by default depth is calculated based on tag name */\n getDepth?: (element: HTMLElement) => number;\n\n /** A function to retrieve heading value, by default `element.textContent` is used */\n getValue?: (element: HTMLElement) => string;\n\n /** Host element or a ref to it to attach scroll event listener, if not provided, `window` is used */\n scrollHost?: HTMLElement | React.RefObject<HTMLElement | null>;\n\n /** Offset from the top of the viewport to use when determining the active heading, `0` by default */\n offset?: number;\n}\n\nexport interface UseScrollSpyReturnValue {\n /** Index of the active heading in the `data` array */\n active: number;\n\n /** Headings data. If not initialize, data is represented by an empty array. */\n data: UseScrollSpyHeadingData[];\n\n /** True if headings value have been retrieved from the DOM. */\n initialized: boolean;\n\n /** Function to update headings values after the parent component has mounted. */\n reinitialize: () => void;\n}\n\nexport function useScrollSpy({\n selector = 'h1, h2, h3, h4, h5, h6',\n getDepth = getDefaultDepth,\n getValue = getDefaultValue,\n offset = 0,\n scrollHost,\n}: UseScrollSpyOptions = {}): UseScrollSpyReturnValue {\n const [active, setActive] = useState(-1);\n const [initialized, setInitialized] = useState(false);\n const [data, setData] = useState<UseScrollSpyHeadingData[]>([]);\n const headingsRef = useRef<UseScrollSpyHeadingData[]>([]);\n\n const handleScroll = useEffectEvent(() => {\n setActive(\n getActiveElement(\n headingsRef.current.map((d) => d.getNode().getBoundingClientRect()),\n offset\n )\n );\n });\n\n const initialize = () => {\n const headings = getHeadingsData(\n Array.from(document.querySelectorAll(selector)),\n getDepth,\n getValue,\n selector\n );\n headingsRef.current = headings;\n setInitialized(true);\n setData(headings);\n setActive(\n getActiveElement(\n headings.map((d) => d.getNode().getBoundingClientRect()),\n offset\n )\n );\n };\n\n useEffect(() => {\n initialize();\n const _scrollHost = resolveScrollHost(scrollHost);\n _scrollHost.addEventListener('scroll', handleScroll);\n return () => _scrollHost.removeEventListener('scroll', handleScroll);\n }, [scrollHost, selector, offset]);\n\n return {\n reinitialize: initialize,\n active,\n initialized,\n data,\n };\n}\n\nexport namespace useScrollSpy {\n export type Options = UseScrollSpyOptions;\n export type ReturnValue = UseScrollSpyReturnValue;\n}\n"],"mappings":";;;;AAGA,SAAS,mBAAmB,SAAsB,UAAkB,OAA4B;CAC9F,IAAI,QAAQ,aACV,OAAO;CAGT,MAAM,WAAW,QAAQ,KAAK,SAAS,eAAe,QAAQ,EAAE,IAAI;CAEpE,IAAI,UACF,OAAO;CAGT,OAAO,SAAS,iBAA8B,QAAQ,CAAC,CAAC,UAAU;AACpE;AAEA,SAAS,gBACP,UACA,UACA,UACA,UAC2B;CAC3B,MAAM,SAAoC,CAAC;CAE3C,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;EAC3C,MAAM,UAAU,SAAS;EACzB,OAAO,KAAK;GACV,OAAO,SAAS,OAAO;GACvB,OAAO,SAAS,OAAO;GACvB,IAAI,QAAQ,MAAMA,kBAAAA,SAAS;GAC3B,eAAe,mBAAmB,SAAS,UAAU,CAAC;EACxD,CAAC;CACH;CAEA,OAAO;AACT;AAEA,SAAS,iBAAiB,OAAkB,SAAiB,GAAG;CAC9D,IAAI,MAAM,WAAW,GACnB,OAAO;CAiBT,OAdgB,MAAM,QACnB,KAAK,MAAM,UAAU;EACpB,IAAI,KAAK,IAAI,IAAI,WAAW,MAAM,IAAI,KAAK,IAAI,KAAK,IAAI,MAAM,GAC5D,OAAO;EAGT,OAAO;GACL;GACA,UAAU,KAAK;EACjB;CACF,GACA;EAAE,OAAO;EAAG,UAAU,MAAM,EAAE,CAAC;CAAE,CAGtB,CAAC,CAAC;AACjB;AAEA,SAAS,gBAAgB,SAAsB;CAC7C,OAAO,OAAO,QAAQ,QAAQ,EAAE;AAClC;AAEA,SAAS,gBAAgB,SAAsB;CAC7C,OAAO,QAAQ,eAAe;AAChC;AAEA,SAAS,kBACP,YACsB;CACtB,IAAI,CAAC,YACH,OAAO;CAGT,IAAI,aAAa,YACf,OAAO,WAAW,WAAW;CAG/B,OAAO;AACT;AA+CA,SAAgB,aAAa,EAC3B,WAAW,0BACX,WAAW,iBACX,WAAW,iBACX,SAAS,GACT,eACuB,CAAC,GAA4B;CACpD,MAAM,CAAC,QAAQ,cAAA,GAAaC,MAAAA,SAAAA,CAAS,EAAE;CACvC,MAAM,CAAC,aAAa,mBAAA,GAAkBA,MAAAA,SAAAA,CAAS,KAAK;CACpD,MAAM,CAAC,MAAM,YAAA,GAAWA,MAAAA,SAAAA,CAAoC,CAAC,CAAC;CAC9D,MAAM,eAAA,GAAcC,MAAAA,OAAAA,CAAkC,CAAC,CAAC;CAExD,MAAM,gBAAA,GAAeC,MAAAA,eAAAA,OAAqB;EACxC,UACE,iBACE,YAAY,QAAQ,KAAK,MAAM,EAAE,QAAQ,CAAC,CAAC,sBAAsB,CAAC,GAClE,MACF,CACF;CACF,CAAC;CAED,MAAM,mBAAmB;EACvB,MAAM,WAAW,gBACf,MAAM,KAAK,SAAS,iBAAiB,QAAQ,CAAC,GAC9C,UACA,UACA,QACF;EACA,YAAY,UAAU;EACtB,eAAe,IAAI;EACnB,QAAQ,QAAQ;EAChB,UACE,iBACE,SAAS,KAAK,MAAM,EAAE,QAAQ,CAAC,CAAC,sBAAsB,CAAC,GACvD,MACF,CACF;CACF;CAEA,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,WAAW;EACX,MAAM,cAAc,kBAAkB,UAAU;EAChD,YAAY,iBAAiB,UAAU,YAAY;EACnD,aAAa,YAAY,oBAAoB,UAAU,YAAY;CACrE,GAAG;EAAC;EAAY;EAAU;CAAM,CAAC;CAEjC,OAAO;EACL,cAAc;EACd;EACA;EACA;CACF;AACF"}
|
|
@@ -12,6 +12,9 @@ function getAutoHeightDuration(height) {
|
|
|
12
12
|
function getElementHeight(elementRef) {
|
|
13
13
|
return elementRef.current ? elementRef.current.scrollHeight : "auto";
|
|
14
14
|
}
|
|
15
|
+
function isMeasured(size) {
|
|
16
|
+
return typeof size === "number" && size > 0;
|
|
17
|
+
}
|
|
15
18
|
function useCollapse({ transitionDuration, transitionTimingFunction = "ease", onTransitionEnd, onTransitionStart, expanded, keepMounted }) {
|
|
16
19
|
const collapsedStyles = {
|
|
17
20
|
height: 0,
|
|
@@ -19,6 +22,7 @@ function useCollapse({ transitionDuration, transitionTimingFunction = "ease", on
|
|
|
19
22
|
...keepMounted ? {} : { display: "none" }
|
|
20
23
|
};
|
|
21
24
|
const onTransitionStartEvent = useEffectEvent(() => onTransitionStart?.());
|
|
25
|
+
const onTransitionEndEvent = useEffectEvent(() => onTransitionEnd?.());
|
|
22
26
|
const elementRef = useRef(null);
|
|
23
27
|
const [styles, setStylesRaw] = useState(expanded ? {} : collapsedStyles);
|
|
24
28
|
const [state, setState] = useState(expanded ? "entered" : "exited");
|
|
@@ -35,9 +39,14 @@ function useCollapse({ transitionDuration, transitionTimingFunction = "ease", on
|
|
|
35
39
|
const duration = transitionDuration ?? getAutoHeightDuration(height);
|
|
36
40
|
return { transition: `height ${duration}ms ${transitionTimingFunction}, opacity ${duration}ms ${transitionTimingFunction}` };
|
|
37
41
|
};
|
|
42
|
+
const transitionRef = useRef(0);
|
|
38
43
|
useDidUpdate(() => {
|
|
44
|
+
transitionRef.current += 1;
|
|
45
|
+
const transitionId = transitionRef.current;
|
|
46
|
+
const isCurrentTransition = () => transitionRef.current === transitionId;
|
|
39
47
|
if (transitionDuration !== 0) onTransitionStartEvent();
|
|
40
48
|
if (expanded) window.requestAnimationFrame(() => {
|
|
49
|
+
if (!isCurrentTransition() || !elementRef.current) return;
|
|
41
50
|
flushSync(() => setState("entering"));
|
|
42
51
|
mergeStyles({
|
|
43
52
|
willChange: "height",
|
|
@@ -45,7 +54,14 @@ function useCollapse({ transitionDuration, transitionTimingFunction = "ease", on
|
|
|
45
54
|
overflow: "hidden"
|
|
46
55
|
});
|
|
47
56
|
window.requestAnimationFrame(() => {
|
|
57
|
+
if (!isCurrentTransition() || !elementRef.current) return;
|
|
48
58
|
const height = getElementHeight(elementRef);
|
|
59
|
+
if (!isMeasured(height)) {
|
|
60
|
+
setStyles({});
|
|
61
|
+
setState("entered");
|
|
62
|
+
onTransitionEndEvent();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
49
65
|
mergeStyles({
|
|
50
66
|
...getTransitionStyles(height),
|
|
51
67
|
height
|
|
@@ -53,17 +69,27 @@ function useCollapse({ transitionDuration, transitionTimingFunction = "ease", on
|
|
|
53
69
|
});
|
|
54
70
|
});
|
|
55
71
|
else window.requestAnimationFrame(() => {
|
|
72
|
+
if (!isCurrentTransition() || !elementRef.current) return;
|
|
56
73
|
flushSync(() => setState("exiting"));
|
|
57
74
|
const height = getElementHeight(elementRef);
|
|
75
|
+
if (!isMeasured(height)) {
|
|
76
|
+
setStyles(collapsedStyles);
|
|
77
|
+
setState("exited");
|
|
78
|
+
onTransitionEndEvent();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
58
81
|
mergeStyles({
|
|
59
82
|
...getTransitionStyles(height),
|
|
60
83
|
willChange: "height",
|
|
61
84
|
height
|
|
62
85
|
});
|
|
63
|
-
window.requestAnimationFrame(() =>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
86
|
+
window.requestAnimationFrame(() => {
|
|
87
|
+
if (!isCurrentTransition() || !elementRef.current) return;
|
|
88
|
+
mergeStyles({
|
|
89
|
+
height: 0,
|
|
90
|
+
overflow: "hidden"
|
|
91
|
+
});
|
|
92
|
+
});
|
|
67
93
|
});
|
|
68
94
|
}, [expanded]);
|
|
69
95
|
const handleTransitionEnd = (event) => {
|
|
@@ -73,11 +99,11 @@ function useCollapse({ transitionDuration, transitionTimingFunction = "ease", on
|
|
|
73
99
|
if (height === styles.height) setStyles({});
|
|
74
100
|
else mergeStyles({ height });
|
|
75
101
|
setState("entered");
|
|
76
|
-
|
|
102
|
+
onTransitionEndEvent();
|
|
77
103
|
} else if (styles.height === 0) {
|
|
78
104
|
setStyles(collapsedStyles);
|
|
79
105
|
setState("exited");
|
|
80
|
-
|
|
106
|
+
onTransitionEndEvent();
|
|
81
107
|
}
|
|
82
108
|
};
|
|
83
109
|
return {
|
|
@@ -96,6 +122,6 @@ function useCollapse({ transitionDuration, transitionTimingFunction = "ease", on
|
|
|
96
122
|
};
|
|
97
123
|
}
|
|
98
124
|
//#endregion
|
|
99
|
-
export { getElementHeight, useCollapse };
|
|
125
|
+
export { getElementHeight, isMeasured, useCollapse };
|
|
100
126
|
|
|
101
127
|
//# sourceMappingURL=use-collapse.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-collapse.mjs","names":[],"sources":["../../src/use-collapse/use-collapse.ts"],"sourcesContent":["import React, { CSSProperties, useEffectEvent, useRef, useState } from 'react';\nimport { flushSync } from 'react-dom';\nimport { useDidUpdate } from '../use-did-update/use-did-update';\nimport { mergeRefs } from '../use-merged-ref/use-merged-ref';\n\nfunction getAutoHeightDuration(height: number | string) {\n if (!height || typeof height === 'string') {\n return 0;\n }\n const constant = height / 36;\n return Math.round((4 + 15 * constant ** 0.25 + constant / 5) * 10);\n}\n\nexport function getElementHeight(elementRef: React.RefObject<HTMLElement | null>) {\n return elementRef.current ? elementRef.current.scrollHeight : 'auto';\n}\n\nexport interface UseCollapseInput {\n /** Expanded state */\n expanded: boolean;\n\n /** Transition duration in milliseconds, by default calculated based on content height */\n transitionDuration?: number;\n\n /** Transition timing function, `ease` by default */\n transitionTimingFunction?: string;\n\n /** Called when transition ends */\n onTransitionEnd?: () => void;\n\n /** Called when transition starts */\n onTransitionStart?: () => void;\n\n /** If true, collapsed content is kept in the DOM and hidden with `display: none` styles */\n keepMounted?: boolean;\n}\n\ninterface GetCollapsePropsInput {\n style?: CSSProperties;\n ref?: React.Ref<HTMLDivElement>;\n}\n\ninterface GetCollapsePropsReturnValue {\n 'aria-hidden': boolean;\n inert: boolean;\n ref: React.RefCallback<HTMLDivElement>;\n onTransitionEnd: (event: React.TransitionEvent<Element>) => void;\n style: React.CSSProperties;\n}\n\nexport type UseCollapseState = 'entering' | 'entered' | 'exiting' | 'exited';\n\nexport interface UseCollapseReturnValue {\n /** Current transition state */\n state: UseCollapseState;\n\n /** Props to pass down to the collapsible element */\n getCollapseProps: (input?: GetCollapsePropsInput) => GetCollapsePropsReturnValue;\n}\n\nexport function useCollapse({\n transitionDuration,\n transitionTimingFunction = 'ease',\n onTransitionEnd,\n onTransitionStart,\n expanded,\n keepMounted,\n}: UseCollapseInput): UseCollapseReturnValue {\n const collapsedStyles = {\n height: 0,\n overflow: 'hidden',\n ...(keepMounted ? {} : { display: 'none' }),\n };\n\n const onTransitionStartEvent = useEffectEvent(() => onTransitionStart?.());\n\n const elementRef = useRef<HTMLElement>(null);\n const [styles, setStylesRaw] = useState<CSSProperties>(expanded ? {} : collapsedStyles);\n const [state, setState] = useState<UseCollapseState>(expanded ? 'entered' : 'exited');\n const setStyles = (newStyles: React.SetStateAction<CSSProperties>) => {\n flushSync(() => setStylesRaw(newStyles));\n };\n\n const mergeStyles = (newStyles: CSSProperties) => {\n setStyles((oldStyles) => ({ ...oldStyles, ...newStyles }));\n };\n\n const getTransitionStyles = (height: number | string) => {\n const duration = transitionDuration ?? getAutoHeightDuration(height);\n return {\n transition: `height ${duration}ms ${transitionTimingFunction}, opacity ${duration}ms ${transitionTimingFunction}`,\n };\n };\n\n useDidUpdate(() => {\n const shouldTransition = transitionDuration !== 0;\n\n if (shouldTransition) {\n onTransitionStartEvent();\n }\n\n if (expanded) {\n window.requestAnimationFrame(() => {\n flushSync(() => setState('entering'));\n mergeStyles({ willChange: 'height', display: 'block', overflow: 'hidden' });\n window.requestAnimationFrame(() => {\n const height = getElementHeight(elementRef);\n mergeStyles({ ...getTransitionStyles(height), height });\n });\n });\n } else {\n window.requestAnimationFrame(() => {\n flushSync(() => setState('exiting'));\n const height = getElementHeight(elementRef);\n mergeStyles({ ...getTransitionStyles(height), willChange: 'height', height });\n window.requestAnimationFrame(() => mergeStyles({ height: 0, overflow: 'hidden' }));\n });\n }\n }, [expanded]);\n\n const handleTransitionEnd = (event: React.TransitionEvent): void => {\n if (event.target !== elementRef.current || event.propertyName !== 'height') {\n return;\n }\n\n if (expanded) {\n const height = getElementHeight(elementRef);\n\n if (height === styles.height) {\n setStyles({});\n } else {\n mergeStyles({ height });\n }\n\n setState('entered');\n
|
|
1
|
+
{"version":3,"file":"use-collapse.mjs","names":[],"sources":["../../src/use-collapse/use-collapse.ts"],"sourcesContent":["import React, { CSSProperties, useEffectEvent, useRef, useState } from 'react';\nimport { flushSync } from 'react-dom';\nimport { useDidUpdate } from '../use-did-update/use-did-update';\nimport { mergeRefs } from '../use-merged-ref/use-merged-ref';\n\nfunction getAutoHeightDuration(height: number | string) {\n if (!height || typeof height === 'string') {\n return 0;\n }\n const constant = height / 36;\n return Math.round((4 + 15 * constant ** 0.25 + constant / 5) * 10);\n}\n\nexport function getElementHeight(elementRef: React.RefObject<HTMLElement | null>) {\n return elementRef.current ? elementRef.current.scrollHeight : 'auto';\n}\n\nexport function isMeasured(size: number | string): size is number {\n return typeof size === 'number' && size > 0;\n}\n\nexport interface UseCollapseInput {\n /** Expanded state */\n expanded: boolean;\n\n /** Transition duration in milliseconds, by default calculated based on content height */\n transitionDuration?: number;\n\n /** Transition timing function, `ease` by default */\n transitionTimingFunction?: string;\n\n /** Called when transition ends */\n onTransitionEnd?: () => void;\n\n /** Called when transition starts */\n onTransitionStart?: () => void;\n\n /** If true, collapsed content is kept in the DOM and hidden with `display: none` styles */\n keepMounted?: boolean;\n}\n\ninterface GetCollapsePropsInput {\n style?: CSSProperties;\n ref?: React.Ref<HTMLDivElement>;\n}\n\ninterface GetCollapsePropsReturnValue {\n 'aria-hidden': boolean;\n inert: boolean;\n ref: React.RefCallback<HTMLDivElement>;\n onTransitionEnd: (event: React.TransitionEvent<Element>) => void;\n style: React.CSSProperties;\n}\n\nexport type UseCollapseState = 'entering' | 'entered' | 'exiting' | 'exited';\n\nexport interface UseCollapseReturnValue {\n /** Current transition state */\n state: UseCollapseState;\n\n /** Props to pass down to the collapsible element */\n getCollapseProps: (input?: GetCollapsePropsInput) => GetCollapsePropsReturnValue;\n}\n\nexport function useCollapse({\n transitionDuration,\n transitionTimingFunction = 'ease',\n onTransitionEnd,\n onTransitionStart,\n expanded,\n keepMounted,\n}: UseCollapseInput): UseCollapseReturnValue {\n const collapsedStyles = {\n height: 0,\n overflow: 'hidden',\n ...(keepMounted ? {} : { display: 'none' }),\n };\n\n const onTransitionStartEvent = useEffectEvent(() => onTransitionStart?.());\n const onTransitionEndEvent = useEffectEvent(() => onTransitionEnd?.());\n\n const elementRef = useRef<HTMLElement>(null);\n const [styles, setStylesRaw] = useState<CSSProperties>(expanded ? {} : collapsedStyles);\n const [state, setState] = useState<UseCollapseState>(expanded ? 'entered' : 'exited');\n const setStyles = (newStyles: React.SetStateAction<CSSProperties>) => {\n flushSync(() => setStylesRaw(newStyles));\n };\n\n const mergeStyles = (newStyles: CSSProperties) => {\n setStyles((oldStyles) => ({ ...oldStyles, ...newStyles }));\n };\n\n const getTransitionStyles = (height: number | string) => {\n const duration = transitionDuration ?? getAutoHeightDuration(height);\n return {\n transition: `height ${duration}ms ${transitionTimingFunction}, opacity ${duration}ms ${transitionTimingFunction}`,\n };\n };\n\n const transitionRef = useRef(0);\n\n useDidUpdate(() => {\n transitionRef.current += 1;\n const transitionId = transitionRef.current;\n const isCurrentTransition = () => transitionRef.current === transitionId;\n const shouldTransition = transitionDuration !== 0;\n\n if (shouldTransition) {\n onTransitionStartEvent();\n }\n\n if (expanded) {\n window.requestAnimationFrame(() => {\n if (!isCurrentTransition() || !elementRef.current) {\n return;\n }\n\n flushSync(() => setState('entering'));\n mergeStyles({ willChange: 'height', display: 'block', overflow: 'hidden' });\n window.requestAnimationFrame(() => {\n if (!isCurrentTransition() || !elementRef.current) {\n return;\n }\n\n const height = getElementHeight(elementRef);\n\n if (!isMeasured(height)) {\n setStyles({});\n setState('entered');\n onTransitionEndEvent();\n return;\n }\n\n mergeStyles({ ...getTransitionStyles(height), height });\n });\n });\n } else {\n window.requestAnimationFrame(() => {\n if (!isCurrentTransition() || !elementRef.current) {\n return;\n }\n\n flushSync(() => setState('exiting'));\n const height = getElementHeight(elementRef);\n\n if (!isMeasured(height)) {\n setStyles(collapsedStyles);\n setState('exited');\n onTransitionEndEvent();\n return;\n }\n\n mergeStyles({ ...getTransitionStyles(height), willChange: 'height', height });\n window.requestAnimationFrame(() => {\n if (!isCurrentTransition() || !elementRef.current) {\n return;\n }\n\n mergeStyles({ height: 0, overflow: 'hidden' });\n });\n });\n }\n }, [expanded]);\n\n const handleTransitionEnd = (event: React.TransitionEvent): void => {\n if (event.target !== elementRef.current || event.propertyName !== 'height') {\n return;\n }\n\n if (expanded) {\n const height = getElementHeight(elementRef);\n\n if (height === styles.height) {\n setStyles({});\n } else {\n mergeStyles({ height });\n }\n\n setState('entered');\n onTransitionEndEvent();\n } else if (styles.height === 0) {\n setStyles(collapsedStyles);\n setState('exited');\n onTransitionEndEvent();\n }\n };\n\n return {\n state,\n getCollapseProps: (input) => ({\n 'aria-hidden': !expanded,\n inert: !expanded,\n ref: mergeRefs(elementRef, input?.ref),\n onTransitionEnd: handleTransitionEnd,\n style: { boxSizing: 'border-box', ...input?.style, ...styles },\n }),\n };\n}\n\nexport namespace useCollapse {\n export type Input = UseCollapseInput;\n export type ReturnValue = UseCollapseReturnValue;\n export type State = UseCollapseState;\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useDidUpdate } from "../use-did-update/use-did-update.mjs";
|
|
3
3
|
import { mergeRefs } from "../use-merged-ref/use-merged-ref.mjs";
|
|
4
|
+
import { isMeasured } from "./use-collapse.mjs";
|
|
4
5
|
import { useEffectEvent, useRef, useState } from "react";
|
|
5
6
|
import { flushSync } from "react-dom";
|
|
6
7
|
//#region packages/@mantine/hooks/src/use-collapse/use-horizontal-collapse.ts
|
|
@@ -19,6 +20,7 @@ function useHorizontalCollapse({ transitionDuration, transitionTimingFunction =
|
|
|
19
20
|
...keepMounted ? {} : { display: "none" }
|
|
20
21
|
};
|
|
21
22
|
const onTransitionStartEvent = useEffectEvent(() => onTransitionStart?.());
|
|
23
|
+
const onTransitionEndEvent = useEffectEvent(() => onTransitionEnd?.());
|
|
22
24
|
const elementRef = useRef(null);
|
|
23
25
|
const [styles, setStylesRaw] = useState(expanded ? {} : collapsedStyles);
|
|
24
26
|
const [state, setState] = useState(expanded ? "entered" : "exited");
|
|
@@ -35,9 +37,14 @@ function useHorizontalCollapse({ transitionDuration, transitionTimingFunction =
|
|
|
35
37
|
const duration = transitionDuration ?? getAutoWidthDuration(width);
|
|
36
38
|
return { transition: `width ${duration}ms ${transitionTimingFunction}, opacity ${duration}ms ${transitionTimingFunction}` };
|
|
37
39
|
};
|
|
40
|
+
const transitionRef = useRef(0);
|
|
38
41
|
useDidUpdate(() => {
|
|
42
|
+
transitionRef.current += 1;
|
|
43
|
+
const transitionId = transitionRef.current;
|
|
44
|
+
const isCurrentTransition = () => transitionRef.current === transitionId;
|
|
39
45
|
if (transitionDuration !== 0) onTransitionStartEvent();
|
|
40
46
|
if (expanded) window.requestAnimationFrame(() => {
|
|
47
|
+
if (!isCurrentTransition() || !elementRef.current) return;
|
|
41
48
|
flushSync(() => setState("entering"));
|
|
42
49
|
mergeStyles({
|
|
43
50
|
willChange: "width",
|
|
@@ -45,7 +52,14 @@ function useHorizontalCollapse({ transitionDuration, transitionTimingFunction =
|
|
|
45
52
|
overflow: "hidden"
|
|
46
53
|
});
|
|
47
54
|
window.requestAnimationFrame(() => {
|
|
55
|
+
if (!isCurrentTransition() || !elementRef.current) return;
|
|
48
56
|
const width = getElementWidth(elementRef);
|
|
57
|
+
if (!isMeasured(width)) {
|
|
58
|
+
setStyles({});
|
|
59
|
+
setState("entered");
|
|
60
|
+
onTransitionEndEvent();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
49
63
|
mergeStyles({
|
|
50
64
|
...getTransitionStyles(width),
|
|
51
65
|
width
|
|
@@ -53,17 +67,27 @@ function useHorizontalCollapse({ transitionDuration, transitionTimingFunction =
|
|
|
53
67
|
});
|
|
54
68
|
});
|
|
55
69
|
else window.requestAnimationFrame(() => {
|
|
70
|
+
if (!isCurrentTransition() || !elementRef.current) return;
|
|
56
71
|
flushSync(() => setState("exiting"));
|
|
57
72
|
const width = getElementWidth(elementRef);
|
|
73
|
+
if (!isMeasured(width)) {
|
|
74
|
+
setStyles(collapsedStyles);
|
|
75
|
+
setState("exited");
|
|
76
|
+
onTransitionEndEvent();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
58
79
|
mergeStyles({
|
|
59
80
|
...getTransitionStyles(width),
|
|
60
81
|
willChange: "width",
|
|
61
82
|
width
|
|
62
83
|
});
|
|
63
|
-
window.requestAnimationFrame(() =>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
84
|
+
window.requestAnimationFrame(() => {
|
|
85
|
+
if (!isCurrentTransition() || !elementRef.current) return;
|
|
86
|
+
mergeStyles({
|
|
87
|
+
width: 0,
|
|
88
|
+
overflow: "hidden"
|
|
89
|
+
});
|
|
90
|
+
});
|
|
67
91
|
});
|
|
68
92
|
}, [expanded]);
|
|
69
93
|
const handleTransitionEnd = (event) => {
|
|
@@ -73,11 +97,11 @@ function useHorizontalCollapse({ transitionDuration, transitionTimingFunction =
|
|
|
73
97
|
if (width === styles.width) setStyles({});
|
|
74
98
|
else mergeStyles({ width });
|
|
75
99
|
setState("entered");
|
|
76
|
-
|
|
100
|
+
onTransitionEndEvent();
|
|
77
101
|
} else if (styles.width === 0) {
|
|
78
102
|
setStyles(collapsedStyles);
|
|
79
103
|
setState("exited");
|
|
80
|
-
|
|
104
|
+
onTransitionEndEvent();
|
|
81
105
|
}
|
|
82
106
|
};
|
|
83
107
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-horizontal-collapse.mjs","names":[],"sources":["../../src/use-collapse/use-horizontal-collapse.ts"],"sourcesContent":["import React, { CSSProperties, useEffectEvent, useRef, useState } from 'react';\nimport { flushSync } from 'react-dom';\nimport { useDidUpdate } from '../use-did-update/use-did-update';\nimport { mergeRefs } from '../use-merged-ref/use-merged-ref';\n\nfunction getAutoWidthDuration(width: number | string) {\n if (!width || typeof width === 'string') {\n return 0;\n }\n const constant = width / 36;\n return Math.round((4 + 15 * constant ** 0.25 + constant / 5) * 10);\n}\n\nexport function getElementWidth(elementRef: React.RefObject<HTMLElement | null>) {\n return elementRef.current ? elementRef.current.scrollWidth : 'auto';\n}\n\nexport interface UseHorizontalCollapseInput {\n /** Expanded state */\n expanded: boolean;\n\n /** Transition duration in milliseconds, by default calculated based on content width */\n transitionDuration?: number;\n\n /** Transition timing function, `ease` by default */\n transitionTimingFunction?: string;\n\n /** Called when transition ends */\n onTransitionEnd?: () => void;\n\n /** Called when transition starts */\n onTransitionStart?: () => void;\n\n /** If true, collapsed content is kept in the DOM and hidden with `display: none` styles */\n keepMounted?: boolean;\n}\n\ninterface GetHorizontalCollapsePropsInput {\n style?: CSSProperties;\n ref?: React.Ref<HTMLDivElement>;\n}\n\ninterface GetHorizontalCollapsePropsReturnValue {\n 'aria-hidden': boolean;\n inert: boolean;\n ref: React.RefCallback<HTMLDivElement>;\n onTransitionEnd: (event: React.TransitionEvent<Element>) => void;\n style: React.CSSProperties;\n}\n\nexport type UseHorizontalCollapseState = 'entering' | 'entered' | 'exiting' | 'exited';\n\nexport interface UseHorizontalCollapseReturnValue {\n /** Current transition state */\n state: UseHorizontalCollapseState;\n\n /** Props to pass down to the collapsible element */\n getCollapseProps: (\n input?: GetHorizontalCollapsePropsInput\n ) => GetHorizontalCollapsePropsReturnValue;\n}\n\nexport function useHorizontalCollapse({\n transitionDuration,\n transitionTimingFunction = 'ease',\n onTransitionEnd,\n onTransitionStart,\n expanded,\n keepMounted,\n}: UseHorizontalCollapseInput): UseHorizontalCollapseReturnValue {\n const collapsedStyles = {\n width: 0,\n overflow: 'hidden',\n ...(keepMounted ? {} : { display: 'none' }),\n };\n\n const onTransitionStartEvent = useEffectEvent(() => onTransitionStart?.());\n\n const elementRef = useRef<HTMLElement>(null);\n const [styles, setStylesRaw] = useState<CSSProperties>(expanded ? {} : collapsedStyles);\n const [state, setState] = useState<UseHorizontalCollapseState>(expanded ? 'entered' : 'exited');\n const setStyles = (newStyles: React.SetStateAction<CSSProperties>) => {\n flushSync(() => setStylesRaw(newStyles));\n };\n\n const mergeStyles = (newStyles: CSSProperties) => {\n setStyles((oldStyles) => ({ ...oldStyles, ...newStyles }));\n };\n\n const getTransitionStyles = (width: number | string) => {\n const duration = transitionDuration ?? getAutoWidthDuration(width);\n return {\n transition: `width ${duration}ms ${transitionTimingFunction}, opacity ${duration}ms ${transitionTimingFunction}`,\n };\n };\n\n useDidUpdate(() => {\n const shouldTransition = transitionDuration !== 0;\n\n if (shouldTransition) {\n onTransitionStartEvent();\n }\n\n if (expanded) {\n window.requestAnimationFrame(() => {\n flushSync(() => setState('entering'));\n mergeStyles({ willChange: 'width', display: 'block', overflow: 'hidden' });\n window.requestAnimationFrame(() => {\n const width = getElementWidth(elementRef);\n mergeStyles({ ...getTransitionStyles(width), width });\n });\n });\n } else {\n window.requestAnimationFrame(() => {\n flushSync(() => setState('exiting'));\n const width = getElementWidth(elementRef);\n mergeStyles({ ...getTransitionStyles(width), willChange: 'width', width });\n window.requestAnimationFrame(() => mergeStyles({ width: 0, overflow: 'hidden' }));\n });\n }\n }, [expanded]);\n\n const handleTransitionEnd = (event: React.TransitionEvent): void => {\n if (event.target !== elementRef.current || event.propertyName !== 'width') {\n return;\n }\n\n if (expanded) {\n const width = getElementWidth(elementRef);\n\n if (width === styles.width) {\n setStyles({});\n } else {\n mergeStyles({ width });\n }\n\n setState('entered');\n
|
|
1
|
+
{"version":3,"file":"use-horizontal-collapse.mjs","names":[],"sources":["../../src/use-collapse/use-horizontal-collapse.ts"],"sourcesContent":["import React, { CSSProperties, useEffectEvent, useRef, useState } from 'react';\nimport { flushSync } from 'react-dom';\nimport { useDidUpdate } from '../use-did-update/use-did-update';\nimport { mergeRefs } from '../use-merged-ref/use-merged-ref';\nimport { isMeasured } from './use-collapse';\n\nfunction getAutoWidthDuration(width: number | string) {\n if (!width || typeof width === 'string') {\n return 0;\n }\n const constant = width / 36;\n return Math.round((4 + 15 * constant ** 0.25 + constant / 5) * 10);\n}\n\nexport function getElementWidth(elementRef: React.RefObject<HTMLElement | null>) {\n return elementRef.current ? elementRef.current.scrollWidth : 'auto';\n}\n\nexport interface UseHorizontalCollapseInput {\n /** Expanded state */\n expanded: boolean;\n\n /** Transition duration in milliseconds, by default calculated based on content width */\n transitionDuration?: number;\n\n /** Transition timing function, `ease` by default */\n transitionTimingFunction?: string;\n\n /** Called when transition ends */\n onTransitionEnd?: () => void;\n\n /** Called when transition starts */\n onTransitionStart?: () => void;\n\n /** If true, collapsed content is kept in the DOM and hidden with `display: none` styles */\n keepMounted?: boolean;\n}\n\ninterface GetHorizontalCollapsePropsInput {\n style?: CSSProperties;\n ref?: React.Ref<HTMLDivElement>;\n}\n\ninterface GetHorizontalCollapsePropsReturnValue {\n 'aria-hidden': boolean;\n inert: boolean;\n ref: React.RefCallback<HTMLDivElement>;\n onTransitionEnd: (event: React.TransitionEvent<Element>) => void;\n style: React.CSSProperties;\n}\n\nexport type UseHorizontalCollapseState = 'entering' | 'entered' | 'exiting' | 'exited';\n\nexport interface UseHorizontalCollapseReturnValue {\n /** Current transition state */\n state: UseHorizontalCollapseState;\n\n /** Props to pass down to the collapsible element */\n getCollapseProps: (\n input?: GetHorizontalCollapsePropsInput\n ) => GetHorizontalCollapsePropsReturnValue;\n}\n\nexport function useHorizontalCollapse({\n transitionDuration,\n transitionTimingFunction = 'ease',\n onTransitionEnd,\n onTransitionStart,\n expanded,\n keepMounted,\n}: UseHorizontalCollapseInput): UseHorizontalCollapseReturnValue {\n const collapsedStyles = {\n width: 0,\n overflow: 'hidden',\n ...(keepMounted ? {} : { display: 'none' }),\n };\n\n const onTransitionStartEvent = useEffectEvent(() => onTransitionStart?.());\n const onTransitionEndEvent = useEffectEvent(() => onTransitionEnd?.());\n\n const elementRef = useRef<HTMLElement>(null);\n const [styles, setStylesRaw] = useState<CSSProperties>(expanded ? {} : collapsedStyles);\n const [state, setState] = useState<UseHorizontalCollapseState>(expanded ? 'entered' : 'exited');\n const setStyles = (newStyles: React.SetStateAction<CSSProperties>) => {\n flushSync(() => setStylesRaw(newStyles));\n };\n\n const mergeStyles = (newStyles: CSSProperties) => {\n setStyles((oldStyles) => ({ ...oldStyles, ...newStyles }));\n };\n\n const getTransitionStyles = (width: number | string) => {\n const duration = transitionDuration ?? getAutoWidthDuration(width);\n return {\n transition: `width ${duration}ms ${transitionTimingFunction}, opacity ${duration}ms ${transitionTimingFunction}`,\n };\n };\n\n const transitionRef = useRef(0);\n\n useDidUpdate(() => {\n transitionRef.current += 1;\n const transitionId = transitionRef.current;\n const isCurrentTransition = () => transitionRef.current === transitionId;\n const shouldTransition = transitionDuration !== 0;\n\n if (shouldTransition) {\n onTransitionStartEvent();\n }\n\n if (expanded) {\n window.requestAnimationFrame(() => {\n if (!isCurrentTransition() || !elementRef.current) {\n return;\n }\n\n flushSync(() => setState('entering'));\n mergeStyles({ willChange: 'width', display: 'block', overflow: 'hidden' });\n window.requestAnimationFrame(() => {\n if (!isCurrentTransition() || !elementRef.current) {\n return;\n }\n\n const width = getElementWidth(elementRef);\n\n if (!isMeasured(width)) {\n setStyles({});\n setState('entered');\n onTransitionEndEvent();\n return;\n }\n\n mergeStyles({ ...getTransitionStyles(width), width });\n });\n });\n } else {\n window.requestAnimationFrame(() => {\n if (!isCurrentTransition() || !elementRef.current) {\n return;\n }\n\n flushSync(() => setState('exiting'));\n const width = getElementWidth(elementRef);\n\n if (!isMeasured(width)) {\n setStyles(collapsedStyles);\n setState('exited');\n onTransitionEndEvent();\n return;\n }\n\n mergeStyles({ ...getTransitionStyles(width), willChange: 'width', width });\n window.requestAnimationFrame(() => {\n if (!isCurrentTransition() || !elementRef.current) {\n return;\n }\n\n mergeStyles({ width: 0, overflow: 'hidden' });\n });\n });\n }\n }, [expanded]);\n\n const handleTransitionEnd = (event: React.TransitionEvent): void => {\n if (event.target !== elementRef.current || event.propertyName !== 'width') {\n return;\n }\n\n if (expanded) {\n const width = getElementWidth(elementRef);\n\n if (width === styles.width) {\n setStyles({});\n } else {\n mergeStyles({ width });\n }\n\n setState('entered');\n onTransitionEndEvent();\n } else if (styles.width === 0) {\n setStyles(collapsedStyles);\n setState('exited');\n onTransitionEndEvent();\n }\n };\n\n return {\n state,\n getCollapseProps: (input) => ({\n 'aria-hidden': !expanded,\n inert: !expanded,\n ref: mergeRefs(elementRef, input?.ref),\n onTransitionEnd: handleTransitionEnd,\n style: { boxSizing: 'border-box', ...input?.style, ...styles },\n }),\n };\n}\n\nexport namespace useHorizontalCollapse {\n export type Input = UseHorizontalCollapseInput;\n export type ReturnValue = UseHorizontalCollapseReturnValue;\n export type State = UseHorizontalCollapseState;\n}\n"],"mappings":"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"}
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useEffect, useRef } from "react";
|
|
3
3
|
//#region packages/@mantine/hooks/src/use-did-update/use-did-update.ts
|
|
4
|
+
function dependenciesChanged(previous, next) {
|
|
5
|
+
return previous.length !== next.length || next.some((dep, index) => !Object.is(dep, previous[index]));
|
|
6
|
+
}
|
|
4
7
|
function useDidUpdate(fn, dependencies) {
|
|
5
8
|
const mounted = useRef(false);
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
+
const previousRender = useRef(null);
|
|
10
|
+
const previousDependencies = useRef(void 0);
|
|
11
|
+
const render = {};
|
|
9
12
|
useEffect(() => {
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
const isSameRender = previousRender.current === render;
|
|
14
|
+
const previous = previousDependencies.current;
|
|
15
|
+
previousRender.current = render;
|
|
16
|
+
previousDependencies.current = dependencies;
|
|
17
|
+
if (!mounted.current) {
|
|
18
|
+
mounted.current = true;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (isSameRender) return;
|
|
22
|
+
if (dependencies && previous && !dependenciesChanged(previous, dependencies)) return;
|
|
23
|
+
return fn();
|
|
12
24
|
}, dependencies);
|
|
13
25
|
}
|
|
14
26
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-did-update.mjs","names":[],"sources":["../../src/use-did-update/use-did-update.ts"],"sourcesContent":["import { DependencyList, EffectCallback, useEffect, useRef } from 'react';\n\nexport function useDidUpdate(fn: EffectCallback, dependencies?: DependencyList) {\n const mounted = useRef(false);\n
|
|
1
|
+
{"version":3,"file":"use-did-update.mjs","names":[],"sources":["../../src/use-did-update/use-did-update.ts"],"sourcesContent":["import { DependencyList, EffectCallback, useEffect, useRef } from 'react';\n\nfunction dependenciesChanged(previous: DependencyList, next: DependencyList) {\n return (\n previous.length !== next.length || next.some((dep, index) => !Object.is(dep, previous[index]))\n );\n}\n\nexport function useDidUpdate(fn: EffectCallback, dependencies?: DependencyList) {\n const mounted = useRef(false);\n const previousRender = useRef<object>(null);\n const previousDependencies = useRef<DependencyList | undefined>(undefined);\n const render = {};\n\n useEffect(() => {\n const isSameRender = previousRender.current === render;\n const previous = previousDependencies.current;\n\n previousRender.current = render;\n previousDependencies.current = dependencies;\n\n if (!mounted.current) {\n mounted.current = true;\n return undefined;\n }\n\n if (isSameRender) {\n return undefined;\n }\n\n if (dependencies && previous && !dependenciesChanged(previous, dependencies)) {\n return undefined;\n }\n\n return fn();\n }, dependencies);\n}\n"],"mappings":";;;AAEA,SAAS,oBAAoB,UAA0B,MAAsB;CAC3E,OACE,SAAS,WAAW,KAAK,UAAU,KAAK,MAAM,KAAK,UAAU,CAAC,OAAO,GAAG,KAAK,SAAS,MAAM,CAAC;AAEjG;AAEA,SAAgB,aAAa,IAAoB,cAA+B;CAC9E,MAAM,UAAU,OAAO,KAAK;CAC5B,MAAM,iBAAiB,OAAe,IAAI;CAC1C,MAAM,uBAAuB,OAAmC,KAAA,CAAS;CACzE,MAAM,SAAS,CAAC;CAEhB,gBAAgB;EACd,MAAM,eAAe,eAAe,YAAY;EAChD,MAAM,WAAW,qBAAqB;EAEtC,eAAe,UAAU;EACzB,qBAAqB,UAAU;EAE/B,IAAI,CAAC,QAAQ,SAAS;GACpB,QAAQ,UAAU;GAClB;EACF;EAEA,IAAI,cACF;EAGF,IAAI,gBAAgB,YAAY,CAAC,oBAAoB,UAAU,YAAY,GACzE;EAGF,OAAO,GAAG;CACZ,GAAG,YAAY;AACjB"}
|