@stack-spot/portal-layout 0.0.52 → 0.0.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Layout.d.ts +2 -2
- package/dist/Layout.js +1 -1
- package/dist/LayoutOverlayManager.js +6 -6
- package/dist/LayoutOverlayManager.js.map +1 -1
- package/dist/components/Dialog.d.ts +1 -1
- package/dist/components/Dialog.js +1 -1
- package/dist/components/Header.d.ts +1 -1
- package/dist/components/Header.js +1 -1
- package/dist/components/OverlayContent.d.ts +1 -1
- package/dist/components/OverlayContent.js +20 -20
- package/dist/components/PortalSwitcher.d.ts +1 -1
- package/dist/components/PortalSwitcher.js +54 -54
- package/dist/components/SelectionList.d.ts +1 -1
- package/dist/components/SelectionList.js +54 -54
- package/dist/components/SelectionList.js.map +1 -1
- package/dist/components/Toaster.d.ts +1 -1
- package/dist/components/Toaster.js +1 -1
- package/dist/components/UserMenu.d.ts +1 -1
- package/dist/components/UserMenu.js +41 -41
- package/dist/components/error/ErrorBoundary.d.ts +1 -1
- package/dist/components/error/ErrorBoundary.js +1 -1
- package/dist/components/error/ErrorFeedback.d.ts +1 -1
- package/dist/components/error/ErrorFeedback.js +1 -1
- package/dist/components/error/SilentErrorBoundary.d.ts +1 -1
- package/dist/components/error/SilentErrorBoundary.js +1 -1
- package/dist/components/menu/MenuContent.d.ts +12 -10
- package/dist/components/menu/MenuContent.d.ts.map +1 -1
- package/dist/components/menu/MenuContent.js +146 -146
- package/dist/components/menu/MenuContent.js.map +1 -1
- package/dist/components/menu/MenuSections.d.ts +1 -1
- package/dist/components/menu/MenuSections.js +1 -1
- package/dist/components/menu/MenuSections.js.map +1 -1
- package/dist/components/menu/PageSelector.d.ts +1 -1
- package/dist/components/menu/PageSelector.js +65 -65
- package/dist/components/menu/PageSelector.js.map +1 -1
- package/dist/components/menu/use-check-text-overflow.js.map +1 -1
- package/dist/layout-context.d.ts +1 -1
- package/dist/layout-context.js +1 -1
- package/dist/layout.css +466 -466
- package/dist/svg/AI.d.ts +1 -1
- package/dist/svg/AI.js +1 -1
- package/dist/svg/EDP.d.ts +1 -1
- package/dist/svg/EDP.js +1 -1
- package/dist/svg/Forbidden.d.ts +1 -1
- package/dist/svg/Forbidden.js +1 -1
- package/dist/svg/HUB.d.ts +1 -1
- package/dist/svg/HUB.js +1 -1
- package/dist/svg/Logo.d.ts +1 -1
- package/dist/svg/Logo.js +1 -1
- package/dist/svg/NotFound.d.ts +1 -1
- package/dist/svg/NotFound.js +1 -1
- package/dist/svg/ServerError.d.ts +1 -1
- package/dist/svg/ServerError.js +1 -1
- package/dist/svg/Unauthenticated.d.ts +1 -1
- package/dist/svg/Unauthenticated.js +1 -1
- package/dist/toaster.js +2 -2
- package/dist/toaster.js.map +1 -1
- package/dist/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/Layout.tsx +106 -106
- package/src/LayoutOverlayManager.tsx +273 -273
- package/src/components/Dialog.tsx +93 -93
- package/src/components/Header.tsx +34 -34
- package/src/components/OverlayContent.tsx +58 -58
- package/src/components/PortalSwitcher.tsx +147 -147
- package/src/components/SelectionList.tsx +272 -272
- package/src/components/Toaster.tsx +16 -16
- package/src/components/UserMenu.tsx +111 -111
- package/src/components/error/ErrorBoundary.tsx +38 -38
- package/src/components/error/ErrorFeedback.tsx +114 -114
- package/src/components/error/ErrorManager.ts +31 -31
- package/src/components/error/SilentErrorBoundary.tsx +54 -54
- package/src/components/menu/MenuContent.tsx +296 -296
- package/src/components/menu/MenuSections.tsx +270 -270
- package/src/components/menu/PageSelector.tsx +154 -154
- package/src/components/menu/constants.ts +2 -2
- package/src/components/menu/types.ts +112 -112
- package/src/components/menu/use-check-text-overflow.tsx +26 -26
- package/src/components/menu/use-keyboard-controls.tsx +70 -70
- package/src/components/types.ts +15 -15
- package/src/dictionary.ts +25 -25
- package/src/elements.ts +24 -24
- package/src/errors.ts +11 -11
- package/src/index.ts +17 -17
- package/src/layout-context.tsx +22 -22
- package/src/layout.css +466 -466
- package/src/svg/AI.tsx +37 -37
- package/src/svg/EDP.tsx +35 -35
- package/src/svg/Forbidden.tsx +22 -22
- package/src/svg/HUB.tsx +35 -35
- package/src/svg/Logo.tsx +35 -35
- package/src/svg/NotFound.tsx +16 -16
- package/src/svg/ServerError.tsx +33 -33
- package/src/svg/Unauthenticated.tsx +16 -16
- package/src/toaster.tsx +76 -76
- package/src/utils.ts +114 -114
- package/tsconfig.json +8 -8
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
import { useCallback, useRef } from 'react'
|
|
2
|
-
|
|
3
|
-
interface Props {
|
|
4
|
-
/**
|
|
5
|
-
* A query selector that returns every html element that must be navigable through the keyboard.
|
|
6
|
-
*/
|
|
7
|
-
querySelectors: string,
|
|
8
|
-
/**
|
|
9
|
-
* Function to call when ESC is pressed. or when TAB is pressed at the last item in the list of items returned by the query selector.
|
|
10
|
-
*/
|
|
11
|
-
onPressEscape?: () => void,
|
|
12
|
-
/**
|
|
13
|
-
* Function to call when TAB is pressed at the last item in the list of items returned by the query selector. Will be the same as
|
|
14
|
-
* onPressEscape if not specified.
|
|
15
|
-
*/
|
|
16
|
-
onPressLastTab?: () => void,
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function useKeyboardControls({ querySelectors, onPressEscape, onPressLastTab = onPressEscape }: Props) {
|
|
20
|
-
const keyboardControlledElement = useRef<HTMLDivElement>(null)
|
|
21
|
-
const listeners = useRef<Pick<Props, 'onPressEscape' | 'onPressLastTab'>>({})
|
|
22
|
-
listeners.current = { onPressEscape, onPressLastTab }
|
|
23
|
-
|
|
24
|
-
const keyboardControls = useCallback((event: KeyboardEvent) => {
|
|
25
|
-
const target = event?.target as HTMLElement | null
|
|
26
|
-
|
|
27
|
-
function getSelectableAnchors() {
|
|
28
|
-
return keyboardControlledElement.current?.querySelectorAll(querySelectors) ?? []
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function handleArrows(key = event.key) {
|
|
32
|
-
const anchors = getSelectableAnchors()
|
|
33
|
-
let i = 0
|
|
34
|
-
while (i < anchors.length && document.activeElement !== anchors[i]) i++
|
|
35
|
-
const next: any = key === 'ArrowDown' ? (anchors[i + 1] ?? anchors[0]) : (anchors[i - 1] ?? anchors[anchors.length - 1])
|
|
36
|
-
next?.focus?.()
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const handlers: Record<string, (() => void) | undefined> = {
|
|
40
|
-
Escape: () => {
|
|
41
|
-
listeners.current.onPressEscape?.()
|
|
42
|
-
event.stopPropagation()
|
|
43
|
-
event.preventDefault()
|
|
44
|
-
},
|
|
45
|
-
Enter: () => {
|
|
46
|
-
target?.click()
|
|
47
|
-
},
|
|
48
|
-
Tab: () => {
|
|
49
|
-
const anchors = getSelectableAnchors()
|
|
50
|
-
if (document.activeElement === anchors[anchors.length - 1]) listeners.current.onPressLastTab?.()
|
|
51
|
-
else handleArrows('ArrowDown')
|
|
52
|
-
event.preventDefault()
|
|
53
|
-
},
|
|
54
|
-
ArrowUp: handleArrows,
|
|
55
|
-
ArrowDown: handleArrows,
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
handlers[event.key]?.()
|
|
59
|
-
}, [])
|
|
60
|
-
|
|
61
|
-
const attachKeyboardListeners = useCallback(() => {
|
|
62
|
-
document.addEventListener('keydown', keyboardControls)
|
|
63
|
-
}, [])
|
|
64
|
-
|
|
65
|
-
const detachKeyboardListeners = useCallback(() => {
|
|
66
|
-
document.removeEventListener('keydown', keyboardControls)
|
|
67
|
-
}, [])
|
|
68
|
-
|
|
69
|
-
return { keyboardControlledElement, attachKeyboardListeners, detachKeyboardListeners }
|
|
70
|
-
}
|
|
1
|
+
import { useCallback, useRef } from 'react'
|
|
2
|
+
|
|
3
|
+
interface Props {
|
|
4
|
+
/**
|
|
5
|
+
* A query selector that returns every html element that must be navigable through the keyboard.
|
|
6
|
+
*/
|
|
7
|
+
querySelectors: string,
|
|
8
|
+
/**
|
|
9
|
+
* Function to call when ESC is pressed. or when TAB is pressed at the last item in the list of items returned by the query selector.
|
|
10
|
+
*/
|
|
11
|
+
onPressEscape?: () => void,
|
|
12
|
+
/**
|
|
13
|
+
* Function to call when TAB is pressed at the last item in the list of items returned by the query selector. Will be the same as
|
|
14
|
+
* onPressEscape if not specified.
|
|
15
|
+
*/
|
|
16
|
+
onPressLastTab?: () => void,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function useKeyboardControls({ querySelectors, onPressEscape, onPressLastTab = onPressEscape }: Props) {
|
|
20
|
+
const keyboardControlledElement = useRef<HTMLDivElement>(null)
|
|
21
|
+
const listeners = useRef<Pick<Props, 'onPressEscape' | 'onPressLastTab'>>({})
|
|
22
|
+
listeners.current = { onPressEscape, onPressLastTab }
|
|
23
|
+
|
|
24
|
+
const keyboardControls = useCallback((event: KeyboardEvent) => {
|
|
25
|
+
const target = event?.target as HTMLElement | null
|
|
26
|
+
|
|
27
|
+
function getSelectableAnchors() {
|
|
28
|
+
return keyboardControlledElement.current?.querySelectorAll(querySelectors) ?? []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function handleArrows(key = event.key) {
|
|
32
|
+
const anchors = getSelectableAnchors()
|
|
33
|
+
let i = 0
|
|
34
|
+
while (i < anchors.length && document.activeElement !== anchors[i]) i++
|
|
35
|
+
const next: any = key === 'ArrowDown' ? (anchors[i + 1] ?? anchors[0]) : (anchors[i - 1] ?? anchors[anchors.length - 1])
|
|
36
|
+
next?.focus?.()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const handlers: Record<string, (() => void) | undefined> = {
|
|
40
|
+
Escape: () => {
|
|
41
|
+
listeners.current.onPressEscape?.()
|
|
42
|
+
event.stopPropagation()
|
|
43
|
+
event.preventDefault()
|
|
44
|
+
},
|
|
45
|
+
Enter: () => {
|
|
46
|
+
target?.click()
|
|
47
|
+
},
|
|
48
|
+
Tab: () => {
|
|
49
|
+
const anchors = getSelectableAnchors()
|
|
50
|
+
if (document.activeElement === anchors[anchors.length - 1]) listeners.current.onPressLastTab?.()
|
|
51
|
+
else handleArrows('ArrowDown')
|
|
52
|
+
event.preventDefault()
|
|
53
|
+
},
|
|
54
|
+
ArrowUp: handleArrows,
|
|
55
|
+
ArrowDown: handleArrows,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
handlers[event.key]?.()
|
|
59
|
+
}, [])
|
|
60
|
+
|
|
61
|
+
const attachKeyboardListeners = useCallback(() => {
|
|
62
|
+
document.addEventListener('keydown', keyboardControls)
|
|
63
|
+
}, [])
|
|
64
|
+
|
|
65
|
+
const detachKeyboardListeners = useCallback(() => {
|
|
66
|
+
document.removeEventListener('keydown', keyboardControls)
|
|
67
|
+
}, [])
|
|
68
|
+
|
|
69
|
+
return { keyboardControlledElement, attachKeyboardListeners, detachKeyboardListeners }
|
|
70
|
+
}
|
package/src/components/types.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import React, { ReactNode } from 'react'
|
|
2
|
-
|
|
3
|
-
interface CustomLabel {
|
|
4
|
-
id: string,
|
|
5
|
-
element: ReactNode,
|
|
6
|
-
}
|
|
7
|
-
export interface Action {
|
|
8
|
-
label: string | CustomLabel,
|
|
9
|
-
onClick?: () => void,
|
|
10
|
-
href?: string,
|
|
11
|
-
target?: React.AnchorHTMLAttributes<HTMLAnchorElement>['target'],
|
|
12
|
-
iconActive?: React.ReactElement,
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export type PortalAcronym = 'EDP' | 'AI' | 'HUB'
|
|
1
|
+
import React, { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
interface CustomLabel {
|
|
4
|
+
id: string,
|
|
5
|
+
element: ReactNode,
|
|
6
|
+
}
|
|
7
|
+
export interface Action {
|
|
8
|
+
label: string | CustomLabel,
|
|
9
|
+
onClick?: () => void,
|
|
10
|
+
href?: string,
|
|
11
|
+
target?: React.AnchorHTMLAttributes<HTMLAnchorElement>['target'],
|
|
12
|
+
iconActive?: React.ReactElement,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type PortalAcronym = 'EDP' | 'AI' | 'HUB'
|
package/src/dictionary.ts
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import { Dictionary, getLanguage, useTranslate } from '@stack-spot/portal-translate'
|
|
2
|
-
|
|
3
|
-
const dictionary = {
|
|
4
|
-
en: {
|
|
5
|
-
close: 'Close',
|
|
6
|
-
validationLabel: 'Please, confirm the action by typing "$0" below:',
|
|
7
|
-
dismiss: 'Dismiss',
|
|
8
|
-
confirm: 'OK',
|
|
9
|
-
cancel: 'Cancel',
|
|
10
|
-
},
|
|
11
|
-
pt: {
|
|
12
|
-
close: 'Fechar',
|
|
13
|
-
validationLabel: 'Por favor, confirme a ação digitando "$0" no campo abaixo:',
|
|
14
|
-
dismiss: 'Dispensar',
|
|
15
|
-
confirm: 'OK',
|
|
16
|
-
cancel: 'Cancelar',
|
|
17
|
-
},
|
|
18
|
-
} satisfies Dictionary
|
|
19
|
-
|
|
20
|
-
export const useDictionary = () => useTranslate(dictionary)
|
|
21
|
-
|
|
22
|
-
export function getDictionary() {
|
|
23
|
-
const language = getLanguage()
|
|
24
|
-
return dictionary[language]
|
|
25
|
-
}
|
|
1
|
+
import { Dictionary, getLanguage, useTranslate } from '@stack-spot/portal-translate'
|
|
2
|
+
|
|
3
|
+
const dictionary = {
|
|
4
|
+
en: {
|
|
5
|
+
close: 'Close',
|
|
6
|
+
validationLabel: 'Please, confirm the action by typing "$0" below:',
|
|
7
|
+
dismiss: 'Dismiss',
|
|
8
|
+
confirm: 'OK',
|
|
9
|
+
cancel: 'Cancel',
|
|
10
|
+
},
|
|
11
|
+
pt: {
|
|
12
|
+
close: 'Fechar',
|
|
13
|
+
validationLabel: 'Por favor, confirme a ação digitando "$0" no campo abaixo:',
|
|
14
|
+
dismiss: 'Dispensar',
|
|
15
|
+
confirm: 'OK',
|
|
16
|
+
cancel: 'Cancelar',
|
|
17
|
+
},
|
|
18
|
+
} satisfies Dictionary
|
|
19
|
+
|
|
20
|
+
export const useDictionary = () => useTranslate(dictionary)
|
|
21
|
+
|
|
22
|
+
export function getDictionary() {
|
|
23
|
+
const language = getLanguage()
|
|
24
|
+
return dictionary[language]
|
|
25
|
+
}
|
package/src/elements.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
export const elementIds = {
|
|
2
|
-
layout: 'layout',
|
|
3
|
-
backdrop: 'backdrop',
|
|
4
|
-
modal: 'modal',
|
|
5
|
-
rightPanel: 'rightPanel',
|
|
6
|
-
bottomDialog: 'bottomDialog',
|
|
7
|
-
page: 'page',
|
|
8
|
-
content: 'content',
|
|
9
|
-
header: 'header',
|
|
10
|
-
menu: 'menu',
|
|
11
|
-
menuContent: 'menuContent',
|
|
12
|
-
menuSections: 'menuSections',
|
|
13
|
-
accessibilityAnnouncer: 'accessibilityAnnouncer',
|
|
14
|
-
} as const
|
|
15
|
-
|
|
16
|
-
export type LayoutElement = keyof typeof elementIds
|
|
17
|
-
export type LayoutElements = Record<LayoutElement, HTMLElement | null>
|
|
18
|
-
|
|
19
|
-
export function getLayoutElements() {
|
|
20
|
-
return (Object.keys(elementIds) as LayoutElement[]).reduce<LayoutElements>(
|
|
21
|
-
(result, id) => ({ ...result, [id]: document.getElementById(id) }),
|
|
22
|
-
{} as LayoutElements,
|
|
23
|
-
)
|
|
24
|
-
}
|
|
1
|
+
export const elementIds = {
|
|
2
|
+
layout: 'layout',
|
|
3
|
+
backdrop: 'backdrop',
|
|
4
|
+
modal: 'modal',
|
|
5
|
+
rightPanel: 'rightPanel',
|
|
6
|
+
bottomDialog: 'bottomDialog',
|
|
7
|
+
page: 'page',
|
|
8
|
+
content: 'content',
|
|
9
|
+
header: 'header',
|
|
10
|
+
menu: 'menu',
|
|
11
|
+
menuContent: 'menuContent',
|
|
12
|
+
menuSections: 'menuSections',
|
|
13
|
+
accessibilityAnnouncer: 'accessibilityAnnouncer',
|
|
14
|
+
} as const
|
|
15
|
+
|
|
16
|
+
export type LayoutElement = keyof typeof elementIds
|
|
17
|
+
export type LayoutElements = Record<LayoutElement, HTMLElement | null>
|
|
18
|
+
|
|
19
|
+
export function getLayoutElements() {
|
|
20
|
+
return (Object.keys(elementIds) as LayoutElement[]).reduce<LayoutElements>(
|
|
21
|
+
(result, id) => ({ ...result, [id]: document.getElementById(id) }),
|
|
22
|
+
{} as LayoutElements,
|
|
23
|
+
)
|
|
24
|
+
}
|
package/src/errors.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export class LayoutError extends Error {
|
|
2
|
-
constructor(message: string) {
|
|
3
|
-
super(`Layout error: ${message}`)
|
|
4
|
-
}
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export class ElementNotFound extends LayoutError {
|
|
8
|
-
constructor(elementName: string, elementId: string) {
|
|
9
|
-
super(`unable to create ${elementName} because no element with id "${elementId}" was found in the view.`)
|
|
10
|
-
}
|
|
11
|
-
}
|
|
1
|
+
export class LayoutError extends Error {
|
|
2
|
+
constructor(message: string) {
|
|
3
|
+
super(`Layout error: ${message}`)
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class ElementNotFound extends LayoutError {
|
|
8
|
+
constructor(elementName: string, elementId: string) {
|
|
9
|
+
super(`unable to create ${elementName} because no element with id "${elementId}" was found in the view.`)
|
|
10
|
+
}
|
|
11
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
export { Layout, RawLayout } from './Layout'
|
|
2
|
-
export { overlay } from './LayoutOverlayManager'
|
|
3
|
-
export { Dialog } from './components/Dialog'
|
|
4
|
-
export { Toaster } from './components/Toaster'
|
|
5
|
-
export { Header, HeaderProps } from './components/Header'
|
|
6
|
-
export { OverlayContent } from './components/OverlayContent'
|
|
7
|
-
export { PortalSwitcher } from './components/PortalSwitcher'
|
|
8
|
-
export { ListAction, SelectionList, SelectionListProps } from './components/SelectionList'
|
|
9
|
-
export { MenuContent, Title, MenuGroup, ActionItem } from './components/menu/MenuContent'
|
|
10
|
-
export { MenuSections } from './components/menu/MenuSections'
|
|
11
|
-
export * from './components/menu/types'
|
|
12
|
-
export * from './components/types'
|
|
13
|
-
export * from './elements'
|
|
14
|
-
export * from './errors'
|
|
15
|
-
export { Logo as StackspotLogo } from './svg/Logo'
|
|
16
|
-
export * from './utils'
|
|
17
|
-
|
|
1
|
+
export { Layout, RawLayout } from './Layout'
|
|
2
|
+
export { overlay } from './LayoutOverlayManager'
|
|
3
|
+
export { Dialog } from './components/Dialog'
|
|
4
|
+
export { Toaster } from './components/Toaster'
|
|
5
|
+
export { Header, HeaderProps } from './components/Header'
|
|
6
|
+
export { OverlayContent } from './components/OverlayContent'
|
|
7
|
+
export { PortalSwitcher } from './components/PortalSwitcher'
|
|
8
|
+
export { ListAction, SelectionList, SelectionListProps } from './components/SelectionList'
|
|
9
|
+
export { MenuContent, Title, MenuGroup, ActionItem } from './components/menu/MenuContent'
|
|
10
|
+
export { MenuSections } from './components/menu/MenuSections'
|
|
11
|
+
export * from './components/menu/types'
|
|
12
|
+
export * from './components/types'
|
|
13
|
+
export * from './elements'
|
|
14
|
+
export * from './errors'
|
|
15
|
+
export { Logo as StackspotLogo } from './svg/Logo'
|
|
16
|
+
export * from './utils'
|
|
17
|
+
|
package/src/layout-context.tsx
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
/* eslint-disable react-refresh/only-export-components */
|
|
2
|
-
|
|
3
|
-
import { createContext, useContext } from 'react'
|
|
4
|
-
|
|
5
|
-
export type AnchorComponent = (props: React.AnchorHTMLAttributes<HTMLAnchorElement>) => React.ReactElement
|
|
6
|
-
|
|
7
|
-
export interface LayoutContext {
|
|
8
|
-
anchorTag?: AnchorComponent,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const context = createContext<LayoutContext>({})
|
|
12
|
-
|
|
13
|
-
const Anchor: AnchorComponent = props => <a {...props} />
|
|
14
|
-
|
|
15
|
-
export const LayoutProvider = ({ children, ...props }: LayoutContext & { children: React.ReactNode }) => (
|
|
16
|
-
<context.Provider value={props}>{children}</context.Provider>
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
export function useAnchorTag(): AnchorComponent {
|
|
20
|
-
const { anchorTag } = useContext(context)
|
|
21
|
-
return anchorTag ?? Anchor
|
|
22
|
-
}
|
|
1
|
+
/* eslint-disable react-refresh/only-export-components */
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext } from 'react'
|
|
4
|
+
|
|
5
|
+
export type AnchorComponent = (props: React.AnchorHTMLAttributes<HTMLAnchorElement>) => React.ReactElement
|
|
6
|
+
|
|
7
|
+
export interface LayoutContext {
|
|
8
|
+
anchorTag?: AnchorComponent,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const context = createContext<LayoutContext>({})
|
|
12
|
+
|
|
13
|
+
const Anchor: AnchorComponent = props => <a {...props} />
|
|
14
|
+
|
|
15
|
+
export const LayoutProvider = ({ children, ...props }: LayoutContext & { children: React.ReactNode }) => (
|
|
16
|
+
<context.Provider value={props}>{children}</context.Provider>
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
export function useAnchorTag(): AnchorComponent {
|
|
20
|
+
const { anchorTag } = useContext(context)
|
|
21
|
+
return anchorTag ?? Anchor
|
|
22
|
+
}
|