Mirror: React hooks for accessible, common web interactions. UI super powers without the UI.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Run prettier formatting

+264 -82
+1 -1
package.json
··· 54 54 "@rollup/plugin-commonjs": "^19.0.1", 55 55 "@rollup/plugin-node-resolve": "^13.0.2", 56 56 "@types/react": "^17.0.14", 57 - "husky": "^7.0.1", 57 + "husky-v4": "^4.3.8", 58 58 "lint-staged": "^11.0.1", 59 59 "npm-run-all": "^4.1.5", 60 60 "prettier": "^2.1.2",
+17 -4
src/useDialogDismiss.ts
··· 5 5 6 6 const usePriority = makePriorityHook(); 7 7 8 - export function useDialogDismiss<T extends HTMLElement>(ref: Ref<T>, onDismiss: () => void) { 8 + export function useDialogDismiss<T extends HTMLElement>( 9 + ref: Ref<T>, 10 + onDismiss: () => void 11 + ) { 9 12 const hasPriority = usePriority(ref); 10 13 11 14 useLayoutEffect(() => { 12 15 if (!hasPriority) return; 13 16 14 17 function onKey(event: KeyboardEvent) { 15 - if (event.isComposing || event.defaultPrevented || event.code !== 'Escape') return; 18 + if ( 19 + event.isComposing || 20 + event.defaultPrevented || 21 + event.code !== 'Escape' 22 + ) 23 + return; 16 24 event.preventDefault(); 17 25 onDismiss(); 18 - }; 26 + } 19 27 20 28 function onClick(event: MouseEvent | TouchEvent) { 21 - if (!ref.current || contains(ref.current, event.target) || event.defaultPrevented) return; 29 + if ( 30 + !ref.current || 31 + contains(ref.current, event.target) || 32 + event.defaultPrevented 33 + ) 34 + return; 22 35 event.preventDefault(); 23 36 onDismiss(); 24 37 }
+25 -8
src/useDialogFocus.ts
··· 1 1 import { snapshotSelection, restoreSelection } from './utils/selection'; 2 - import { getFirstFocusTarget, getFocusTargets, getNextFocusTarget } from './utils/focus'; 2 + import { 3 + getFirstFocusTarget, 4 + getFocusTargets, 5 + getNextFocusTarget, 6 + } from './utils/focus'; 3 7 import { useLayoutEffect } from './utils/react'; 4 8 import { contains, isInputElement } from './utils/element'; 5 9 import { makePriorityHook } from './usePriority'; ··· 41 45 if (!ref.current || event.defaultPrevented) return; 42 46 43 47 const active = document.activeElement as HTMLElement; 44 - const owner = (ownerRef && ownerRef.current) || selection && selection.element; 48 + const owner = 49 + (ownerRef && ownerRef.current) || (selection && selection.element); 45 50 46 51 if (willReceiveFocus || (owner && event.target === owner)) { 47 - if (!contains(ref.current, active)) selection = snapshotSelection(owner); 52 + if (!contains(ref.current, active)) 53 + selection = snapshotSelection(owner); 48 54 willReceiveFocus = false; 49 55 return; 50 56 } 51 57 52 58 const { relatedTarget, target } = event; 53 59 // Check whether focus is about to move into the container and prevent it 54 - if (contains(ref.current, target) && !contains(ref.current, relatedTarget)) { 60 + if ( 61 + contains(ref.current, target) && 62 + !contains(ref.current, relatedTarget) 63 + ) { 55 64 // Get the next focus target of the container 56 65 const focusTarget = getNextFocusTarget(ref.current, !focusMovesForward); 57 66 if (focusTarget) { ··· 71 80 } 72 81 73 82 const active = document.activeElement as HTMLElement; 74 - const owner = (ownerRef && ownerRef.current) || selection && selection.element; 83 + const owner = 84 + (ownerRef && ownerRef.current) || (selection && selection.element); 75 85 const focusTargets = getFocusTargets(ref.current); 76 86 77 87 if ( ··· 95 105 // Implement forward movement in focus targets 96 106 event.preventDefault(); 97 107 const focusIndex = focusTargets.indexOf(active); 98 - const nextIndex = focusIndex < focusTargets.length - 1 ? focusIndex + 1 : 0; 108 + const nextIndex = 109 + focusIndex < focusTargets.length - 1 ? focusIndex + 1 : 0; 99 110 willReceiveFocus = true; 100 111 focusTargets[nextIndex].focus(); 101 112 } else if ( ··· 105 116 // Implement backward movement in focus targets 106 117 event.preventDefault(); 107 118 const focusIndex = focusTargets.indexOf(active); 108 - const nextIndex = focusIndex > 0 ? focusIndex - 1 : focusTargets.length - 1; 119 + const nextIndex = 120 + focusIndex > 0 ? focusIndex - 1 : focusTargets.length - 1; 109 121 willReceiveFocus = true; 110 122 focusTargets[nextIndex].focus(); 111 123 } else if (selection && event.code === 'Escape') { ··· 113 125 event.preventDefault(); 114 126 willReceiveFocus = false; 115 127 restoreSelection(selection); 116 - } else if (owner && isInputElement(owner) && contains(owner, active) && event.code === 'Enter') { 128 + } else if ( 129 + owner && 130 + isInputElement(owner) && 131 + contains(owner, active) && 132 + event.code === 'Enter' 133 + ) { 117 134 // Move focus to first target when enter is pressed 118 135 event.preventDefault(); 119 136 const newTarget = getFirstFocusTarget(ref.current);
+36 -10
src/useMenuFocus.ts
··· 1 - import { RestoreSelection, snapshotSelection, restoreSelection } from './utils/selection'; 1 + import { 2 + RestoreSelection, 3 + snapshotSelection, 4 + restoreSelection, 5 + } from './utils/selection'; 2 6 import { getFirstFocusTarget, getFocusTargets } from './utils/focus'; 3 7 import { useLayoutEffect } from './utils/react'; 4 8 import { contains, isInputElement } from './utils/element'; ··· 9 13 ownerRef?: Ref<HTMLElement>; 10 14 } 11 15 12 - export function useMenuFocus<T extends HTMLElement>(ref: Ref<T>, options?: MenuFocusOptions) { 16 + export function useMenuFocus<T extends HTMLElement>( 17 + ref: Ref<T>, 18 + options?: MenuFocusOptions 19 + ) { 13 20 const ownerRef = options && options.ownerRef; 14 21 const disabled = !!(options && options.disabled); 15 22 ··· 21 28 function onFocus(event: FocusEvent) { 22 29 if (!ref.current || event.defaultPrevented) return; 23 30 24 - const owner = (ownerRef && ownerRef.current) || selection && selection.element; 31 + const owner = 32 + (ownerRef && ownerRef.current) || (selection && selection.element); 25 33 const { relatedTarget, target } = event; 26 34 if (relatedTarget === owner) { 27 35 // When owner is explicitly passed we can make a snapshot early 28 36 selection = snapshotSelection(owner); 29 - } else if (contains(ref.current, target) && !contains(ref.current, relatedTarget)) { 37 + } else if ( 38 + contains(ref.current, target) && 39 + !contains(ref.current, relatedTarget) 40 + ) { 30 41 // Check whether focus is about to move into the container and snapshot last focus 31 42 selection = snapshotSelection(owner); 32 - } else if (contains(ref.current, relatedTarget) && !contains(ref.current, target)) { 43 + } else if ( 44 + contains(ref.current, relatedTarget) && 45 + !contains(ref.current, target) 46 + ) { 33 47 // Reset focus if it's lost and has left the menu 34 48 selection = null; 35 49 } ··· 38 52 function onKey(event: KeyboardEvent) { 39 53 if (!ref.current || event.defaultPrevented || event.isComposing) return; 40 54 41 - const owner = (ownerRef && ownerRef.current) || selection && selection.element; 55 + const owner = 56 + (ownerRef && ownerRef.current) || (selection && selection.element); 42 57 const active = document.activeElement as HTMLElement; 43 58 const focusTargets = getFocusTargets(ref.current); 44 - if (!focusTargets.length || !contains(ref.current, active) || !contains(owner, active)) { 59 + if ( 60 + !focusTargets.length || 61 + !contains(ref.current, active) || 62 + !contains(owner, active) 63 + ) { 45 64 // Do nothing if container doesn't contain focus or not targets are available 46 65 return; 47 66 } ··· 53 72 // Implement forward movement in focus targets 54 73 event.preventDefault(); 55 74 const focusIndex = focusTargets.indexOf(active); 56 - const nextIndex = focusIndex < focusTargets.length - 1 ? focusIndex + 1 : 0; 75 + const nextIndex = 76 + focusIndex < focusTargets.length - 1 ? focusIndex + 1 : 0; 57 77 focusTargets[nextIndex].focus(); 58 78 } else if ( 59 79 (!isInputElement(active) && event.code === 'ArrowLeft') || ··· 62 82 // Implement backward movement in focus targets 63 83 event.preventDefault(); 64 84 const focusIndex = focusTargets.indexOf(active); 65 - const nextIndex = focusIndex > 0 ? focusIndex - 1 : focusTargets.length - 1; 85 + const nextIndex = 86 + focusIndex > 0 ? focusIndex - 1 : focusTargets.length - 1; 66 87 focusTargets[nextIndex].focus(); 67 88 } else if (event.code === 'Home') { 68 89 // Implement Home => first item ··· 72 93 // Implement End => last item 73 94 event.preventDefault(); 74 95 focusTargets[focusTargets.length - 1].focus(); 75 - } else if (owner && isInputElement(owner) && contains(owner, active) && event.code === 'Enter') { 96 + } else if ( 97 + owner && 98 + isInputElement(owner) && 99 + contains(owner, active) && 100 + event.code === 'Enter' 101 + ) { 76 102 // Move focus to first target when enter is pressed 77 103 event.preventDefault(); 78 104 const newTarget = getFirstFocusTarget(ref.current);
+17 -5
src/useModalFocus.ts
··· 1 - import { RestoreSelection, snapshotSelection, restoreSelection } from './utils/selection'; 1 + import { 2 + RestoreSelection, 3 + snapshotSelection, 4 + restoreSelection, 5 + } from './utils/selection'; 2 6 import { getFirstFocusTarget, getFocusTargets } from './utils/focus'; 3 7 import { useLayoutEffect } from './utils/react'; 4 8 import { contains } from './utils/element'; ··· 11 15 disabled?: boolean; 12 16 } 13 17 14 - export function useModalFocus<T extends HTMLElement>(ref: Ref<T>, options?: ModalFocusOptions) { 18 + export function useModalFocus<T extends HTMLElement>( 19 + ref: Ref<T>, 20 + options?: ModalFocusOptions 21 + ) { 15 22 const disabled = !!(options && options.disabled); 16 23 const hasPriority = usePriority(ref, disabled); 17 24 ··· 19 26 if (!ref.current || !hasPriority || disabled) return; 20 27 21 28 let selection: RestoreSelection | null = null; 22 - if (!document.activeElement || !ref.current.contains(document.activeElement)) { 29 + if ( 30 + !document.activeElement || 31 + !ref.current.contains(document.activeElement) 32 + ) { 23 33 const newTarget = getFirstFocusTarget(ref.current); 24 34 if (newTarget) { 25 35 selection = snapshotSelection(ref.current); ··· 31 41 const parent = ref.current; 32 42 if (!parent || event.defaultPrevented) return; 33 43 34 - if (contains(parent, event.target) && !contains(parent, event.relatedTarget)) { 44 + if ( 45 + contains(parent, event.target) && 46 + !contains(parent, event.relatedTarget) 47 + ) { 35 48 const target = getFirstFocusTarget(parent); 36 49 if (target) target.focus(); 37 50 } ··· 52 65 event.preventDefault(); 53 66 targets[0].focus(); 54 67 } 55 - 56 68 } 57 69 } 58 70
+8 -4
src/usePriority.ts
··· 13 13 (x & 16 /* a contains b */ && -1) || 14 14 (x & 8 /* b contains a */ && 1) || 15 15 (x & 2 /* b follows a */ && -1) || 16 - (x & 4 /* a follows b */ && 1) 17 - ) || 0; 16 + (x & 4 /* a follows b */ && 1) || 17 + 0 18 + ); 18 19 }; 19 20 20 21 /** Indicates whether a given element on a stack of active priority hooks is the deepest element. */ 21 - return function usePriority<T extends HTMLElement>(ref: Ref<T>, disabled?: boolean): boolean { 22 + return function usePriority<T extends HTMLElement>( 23 + ref: Ref<T>, 24 + disabled?: boolean 25 + ): boolean { 22 26 function computeHasPriority(): boolean { 23 27 if (!ref.current) return false; 24 28 const tempStack = priorityStack.concat(ref.current).sort(sortByHierarchy); ··· 51 55 }, [ref, isDisabled]); 52 56 53 57 return hasPriority; 54 - } 58 + }; 55 59 };
+19 -17
src/utils/element.ts
··· 2 2 export const getTabIndex = (node: Element): number => { 3 3 const index = parseInt(node.getAttribute('tabindex')!, 10); 4 4 return ( 5 - index === index && 6 - !(node as HTMLElement).isContentEditable && 7 - index 8 - ) || 0; 5 + (index === index && !(node as HTMLElement).isContentEditable && index) || 0 6 + ); 9 7 }; 10 8 11 9 /** Returns whether an element is visible in the context of focusability. */ 12 - export const isVisible = (node: Element): boolean => !!( 13 - (node as HTMLElement).offsetWidth && 14 - (node as HTMLElement).offsetHeight && 15 - node.getClientRects().length && 16 - getComputedStyle(node).visibility !== 'hidden' 17 - ); 10 + export const isVisible = (node: Element): boolean => 11 + !!( 12 + (node as HTMLElement).offsetWidth && 13 + (node as HTMLElement).offsetHeight && 14 + node.getClientRects().length && 15 + getComputedStyle(node).visibility !== 'hidden' 16 + ); 18 17 19 18 /** Returns whether an element accepts text input. */ 20 - export const isInputElement = (node: Element): boolean => !!( 21 - node.tagName === 'INPUT' 22 - || node.tagName === 'TEXTAREA' 23 - || (node as HTMLElement).isContentEditable 24 - ); 19 + export const isInputElement = (node: Element): boolean => 20 + !!( 21 + node.tagName === 'INPUT' || 22 + node.tagName === 'TEXTAREA' || 23 + (node as HTMLElement).isContentEditable 24 + ); 25 25 26 - export const contains = (owner: Element | null, node: Element | EventTarget | null) => 27 - !!(node && owner && (owner === node || owner.contains(node as Element))); 26 + export const contains = ( 27 + owner: Element | null, 28 + node: Element | EventTarget | null 29 + ) => !!(node && owner && (owner === node || owner.contains(node as Element)));
+23 -11
src/utils/focus.ts
··· 1 1 import { getTabIndex, isVisible } from './element'; 2 2 3 - const excludeSelector = ':not([tabindex^="-"]):not([aria-modal]):not([role="dialog"])'; 3 + const excludeSelector = 4 + ':not([tabindex^="-"]):not([aria-modal]):not([role="dialog"])'; 4 5 5 6 const focusableSelectors = [ 6 7 'input:not([type="hidden"]):not([disabled])' + excludeSelector, ··· 16 17 ].join(','); 17 18 18 19 /** Generic sorting function for tupel containing elements with indices and tab indices. */ 19 - const sortByTabindex = <T extends HTMLElement>(a: [number, number, T], b: [number, number, T]) => { 20 - return a[1] === a[1] 21 - ? a[0] - b[0] 22 - : a[1] - a[1]; 20 + const sortByTabindex = <T extends HTMLElement>( 21 + a: [number, number, T], 22 + b: [number, number, T] 23 + ) => { 24 + return a[1] === a[1] ? a[0] - b[0] : a[1] - a[1]; 23 25 }; 24 26 25 27 /** Returns whether this node may contain focusable elements. */ 26 28 export const hasFocusTargets = (node: Element): boolean => 27 - !node.matches(excludeSelector) && isVisible(node) && !!node.querySelector(focusableSelectors); 29 + !node.matches(excludeSelector) && 30 + isVisible(node) && 31 + !!node.querySelector(focusableSelectors); 28 32 29 33 /** Returns a sorted list of focus targets inside the given element. */ 30 34 export const getFocusTargets = (node: Element): HTMLElement[] => { 31 35 const elements = node.querySelectorAll(focusableSelectors); 32 36 const targets: HTMLElement[] = []; 33 - const tabIndexTargets: [index: number, tabIndex: number, element: HTMLElement][] = []; 37 + const tabIndexTargets: [ 38 + index: number, 39 + tabIndex: number, 40 + element: HTMLElement 41 + ][] = []; 34 42 for (let i = 0, l = elements.length; i < l; i++) { 35 43 const element = elements[i] as HTMLElement; 36 44 if (isVisible(element)) { ··· 55 63 }; 56 64 57 65 /** Returns the next (optionally in reverse) focus target given a target node. */ 58 - export const getNextFocusTarget = (node: HTMLElement, reverse?: boolean): HTMLElement | null => { 66 + export const getNextFocusTarget = ( 67 + node: HTMLElement, 68 + reverse?: boolean 69 + ): HTMLElement | null => { 59 70 let current: Element | null = node; 60 71 while (current) { 61 72 let next: Element | null = current; 62 - while (next = reverse ? next.previousElementSibling : next.nextElementSibling) { 73 + while ( 74 + (next = reverse ? next.previousElementSibling : next.nextElementSibling) 75 + ) { 63 76 if (isVisible(next) && !!node.matches(focusableSelectors)) { 64 77 return next as HTMLElement; 65 78 } else if (hasFocusTargets(next)) { 66 79 const targets = getFocusTargets(next); 67 - if (targets.length) 68 - return targets[reverse ? targets.length - 1 : 0]; 80 + if (targets.length) return targets[reverse ? targets.length - 1 : 0]; 69 81 } 70 82 } 71 83
+2 -3
src/utils/react.ts
··· 1 1 import { useEffect, useLayoutEffect } from 'react'; 2 2 3 - const useIsomorphicEffect = typeof window !== 'undefined' 4 - ? useLayoutEffect 5 - : useEffect; 3 + const useIsomorphicEffect = 4 + typeof window !== 'undefined' ? useLayoutEffect : useEffect; 6 5 7 6 export { useIsomorphicEffect as useLayoutEffect };
+22 -14
src/utils/selection.ts
··· 1 1 interface RestoreInputSelection { 2 - element: HTMLElement, 3 - method: 'setSelectionRange', 4 - arguments: [number, number, 'forward' | 'backward' | 'none' | undefined], 2 + element: HTMLElement; 3 + method: 'setSelectionRange'; 4 + arguments: [number, number, 'forward' | 'backward' | 'none' | undefined]; 5 5 } 6 6 7 7 interface RestoreActiveNode { 8 - element: HTMLElement, 9 - method: 'focus', 8 + element: HTMLElement; 9 + method: 'focus'; 10 10 } 11 11 12 12 interface RestoreSelectionRange { 13 - element: HTMLElement, 14 - method: 'range', 15 - range: Range 13 + element: HTMLElement; 14 + method: 'range'; 15 + range: Range; 16 16 } 17 17 18 - export type RestoreSelection = RestoreInputSelection | RestoreActiveNode | RestoreSelectionRange; 18 + export type RestoreSelection = 19 + | RestoreInputSelection 20 + | RestoreActiveNode 21 + | RestoreSelectionRange; 19 22 20 - const isInputElement = (node: HTMLElement): node is HTMLInputElement => ( 23 + const isInputElement = (node: HTMLElement): node is HTMLInputElement => 21 24 (node.nodeName === 'input' || node.nodeName === 'textarea') && 22 25 typeof (node as HTMLInputElement).selectionStart === 'number' && 23 - typeof (node as HTMLInputElement).selectionEnd === 'number' 24 - ); 26 + typeof (node as HTMLInputElement).selectionEnd === 'number'; 25 27 26 28 /** Snapshots the current focus or selection target, optinally using a ref if it's passed. */ 27 - export const snapshotSelection = (node?: HTMLElement | null): RestoreSelection | null => { 29 + export const snapshotSelection = ( 30 + node?: HTMLElement | null 31 + ): RestoreSelection | null => { 28 32 const target = document.activeElement as HTMLElement | null; 29 33 const element = node && target && node !== target ? node : target; 30 34 if (!element || !target) { ··· 33 37 return { 34 38 element, 35 39 method: 'setSelectionRange', 36 - arguments: [target.selectionStart!, target.selectionEnd!, target.selectionDirection || undefined], 40 + arguments: [ 41 + target.selectionStart!, 42 + target.selectionEnd!, 43 + target.selectionDirection || undefined, 44 + ], 37 45 }; 38 46 } 39 47
+94 -5
yarn.lock
··· 251 251 escape-string-regexp "^1.0.5" 252 252 supports-color "^5.3.0" 253 253 254 - chalk@^4.1.0, chalk@^4.1.1: 254 + chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: 255 255 version "4.1.1" 256 256 resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" 257 257 integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== 258 258 dependencies: 259 259 ansi-styles "^4.1.0" 260 260 supports-color "^7.1.0" 261 + 262 + ci-info@^2.0.0: 263 + version "2.0.0" 264 + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" 265 + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== 261 266 262 267 clean-stack@^2.0.0: 263 268 version "2.2.0" ··· 322 327 version "1.0.1" 323 328 resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" 324 329 integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= 330 + 331 + compare-versions@^3.6.0: 332 + version "3.6.0" 333 + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" 334 + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== 325 335 326 336 concat-map@0.0.1: 327 337 version "0.0.1" ··· 492 502 locate-path "^5.0.0" 493 503 path-exists "^4.0.0" 494 504 505 + find-up@^5.0.0: 506 + version "5.0.0" 507 + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" 508 + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== 509 + dependencies: 510 + locate-path "^6.0.0" 511 + path-exists "^4.0.0" 512 + 513 + find-versions@^4.0.0: 514 + version "4.0.0" 515 + resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-4.0.0.tgz#3c57e573bf97769b8cb8df16934b627915da4965" 516 + integrity sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ== 517 + dependencies: 518 + semver-regex "^3.1.2" 519 + 495 520 fs-extra@8.1.0: 496 521 version "8.1.0" 497 522 resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" ··· 589 614 resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" 590 615 integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== 591 616 592 - husky@^7.0.1: 593 - version "7.0.1" 594 - resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.1.tgz#579f4180b5da4520263e8713cc832942b48e1f1c" 595 - integrity sha512-gceRaITVZ+cJH9sNHqx5tFwbzlLCVxtVZcusME8JYQ8Edy5mpGDOqD8QBCdMhpyo9a+JXddnujQ4rpY2Ff9SJA== 617 + husky-v4@^4.3.8: 618 + version "4.3.8" 619 + resolved "https://registry.yarnpkg.com/husky-v4/-/husky-v4-4.3.8.tgz#af3be56a8b62b941371b5190e265f76dd1af2e57" 620 + integrity sha512-M7A9u/t6BnT/qbDzKb7SdXhr8qLTGTkqZL6YLDDM20jfCdmpIMEuO384LvYXSBcgv50oIgNWI/IaO3g4A4ShjA== 621 + dependencies: 622 + chalk "^4.0.0" 623 + ci-info "^2.0.0" 624 + compare-versions "^3.6.0" 625 + cosmiconfig "^7.0.0" 626 + find-versions "^4.0.0" 627 + opencollective-postinstall "^2.0.2" 628 + pkg-dir "^5.0.0" 629 + please-upgrade-node "^3.2.0" 630 + slash "^3.0.0" 631 + which-pm-runs "^1.0.0" 596 632 597 633 import-fresh@^3.2.1: 598 634 version "3.3.0" ··· 823 859 dependencies: 824 860 p-locate "^4.1.0" 825 861 862 + locate-path@^6.0.0: 863 + version "6.0.0" 864 + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" 865 + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== 866 + dependencies: 867 + p-locate "^5.0.0" 868 + 826 869 log-symbols@^4.1.0: 827 870 version "4.1.0" 828 871 resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" ··· 983 1026 dependencies: 984 1027 mimic-fn "^2.1.0" 985 1028 1029 + opencollective-postinstall@^2.0.2: 1030 + version "2.0.3" 1031 + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" 1032 + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== 1033 + 986 1034 p-limit@^2.2.0: 987 1035 version "2.3.0" 988 1036 resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" ··· 990 1038 dependencies: 991 1039 p-try "^2.0.0" 992 1040 1041 + p-limit@^3.0.2: 1042 + version "3.1.0" 1043 + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" 1044 + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== 1045 + dependencies: 1046 + yocto-queue "^0.1.0" 1047 + 993 1048 p-locate@^4.1.0: 994 1049 version "4.1.0" 995 1050 resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" 996 1051 integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== 997 1052 dependencies: 998 1053 p-limit "^2.2.0" 1054 + 1055 + p-locate@^5.0.0: 1056 + version "5.0.0" 1057 + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" 1058 + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== 1059 + dependencies: 1060 + p-limit "^3.0.2" 999 1061 1000 1062 p-map@^4.0.0: 1001 1063 version "4.0.0" ··· 1093 1155 dependencies: 1094 1156 find-up "^4.0.0" 1095 1157 1158 + pkg-dir@^5.0.0: 1159 + version "5.0.0" 1160 + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" 1161 + integrity sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA== 1162 + dependencies: 1163 + find-up "^5.0.0" 1164 + 1096 1165 please-upgrade-node@^3.2.0: 1097 1166 version "3.2.0" 1098 1167 resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" ··· 1231 1300 resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" 1232 1301 integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= 1233 1302 1303 + semver-regex@^3.1.2: 1304 + version "3.1.2" 1305 + resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.2.tgz#34b4c0d361eef262e07199dbef316d0f2ab11807" 1306 + integrity sha512-bXWyL6EAKOJa81XG1OZ/Yyuq+oT0b2YLlxx7c+mrdYPaPbnj6WgVULXhinMIeZGufuUBu/eVRqXEhiv4imfwxA== 1307 + 1234 1308 "semver@2 || 3 || 4 || 5", semver@^5.5.0: 1235 1309 version "5.7.1" 1236 1310 resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" ··· 1282 1356 resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" 1283 1357 integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== 1284 1358 1359 + slash@^3.0.0: 1360 + version "3.0.0" 1361 + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" 1362 + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== 1363 + 1285 1364 slice-ansi@^3.0.0: 1286 1365 version "3.0.0" 1287 1366 resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" ··· 1526 1605 is-string "^1.0.5" 1527 1606 is-symbol "^1.0.3" 1528 1607 1608 + which-pm-runs@^1.0.0: 1609 + version "1.0.0" 1610 + resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" 1611 + integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= 1612 + 1529 1613 which@^1.2.9: 1530 1614 version "1.3.1" 1531 1615 resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" ··· 1567 1651 version "1.10.2" 1568 1652 resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" 1569 1653 integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== 1654 + 1655 + yocto-queue@^0.1.0: 1656 + version "0.1.0" 1657 + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" 1658 + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==