@xyo-network/react-xns 3.0.10
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/LICENSE +165 -0
- package/README.md +13 -0
- package/dist/browser/components/EstimateName/EstimateNameTextField.d.ts +7 -0
- package/dist/browser/components/EstimateName/EstimateNameTextField.d.ts.map +1 -0
- package/dist/browser/components/EstimateName/index.d.ts +2 -0
- package/dist/browser/components/EstimateName/index.d.ts.map +1 -0
- package/dist/browser/components/XnsNameCapture/Errors.d.ts +8 -0
- package/dist/browser/components/XnsNameCapture/Errors.d.ts.map +1 -0
- package/dist/browser/components/XnsNameCapture/Props.d.ts +34 -0
- package/dist/browser/components/XnsNameCapture/Props.d.ts.map +1 -0
- package/dist/browser/components/XnsNameCapture/SecondaryLink.d.ts +14 -0
- package/dist/browser/components/XnsNameCapture/SecondaryLink.d.ts.map +1 -0
- package/dist/browser/components/XnsNameCapture/XnsNameCapture.d.ts +4 -0
- package/dist/browser/components/XnsNameCapture/XnsNameCapture.d.ts.map +1 -0
- package/dist/browser/components/XnsNameCapture/XnsNameCaptureWithContext.d.ts +4 -0
- package/dist/browser/components/XnsNameCapture/XnsNameCaptureWithContext.d.ts.map +1 -0
- package/dist/browser/components/XnsNameCapture/hooks/index.d.ts +3 -0
- package/dist/browser/components/XnsNameCapture/hooks/index.d.ts.map +1 -0
- package/dist/browser/components/XnsNameCapture/hooks/useXnsNameCaptureProviders.d.ts +399 -0
- package/dist/browser/components/XnsNameCapture/hooks/useXnsNameCaptureProviders.d.ts.map +1 -0
- package/dist/browser/components/XnsNameCapture/hooks/useXnsNameCaptureRouting.d.ts +399 -0
- package/dist/browser/components/XnsNameCapture/hooks/useXnsNameCaptureRouting.d.ts.map +1 -0
- package/dist/browser/components/XnsNameCapture/index.d.ts +7 -0
- package/dist/browser/components/XnsNameCapture/index.d.ts.map +1 -0
- package/dist/browser/components/index.d.ts +3 -0
- package/dist/browser/components/index.d.ts.map +1 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.mjs +276 -0
- package/dist/browser/index.mjs.map +1 -0
- package/package.json +85 -0
- package/src/components/EstimateName/EstimateNameTextField.stories.tsx +15 -0
- package/src/components/EstimateName/EstimateNameTextField.tsx +50 -0
- package/src/components/EstimateName/index.ts +1 -0
- package/src/components/XnsNameCapture/Errors.tsx +55 -0
- package/src/components/XnsNameCapture/Props.ts +50 -0
- package/src/components/XnsNameCapture/SecondaryLink.stories.tsx +18 -0
- package/src/components/XnsNameCapture/SecondaryLink.tsx +66 -0
- package/src/components/XnsNameCapture/XnsNameCapture.stories.tsx +19 -0
- package/src/components/XnsNameCapture/XnsNameCapture.tsx +114 -0
- package/src/components/XnsNameCapture/XnsNameCaptureWithContext.stories.tsx +32 -0
- package/src/components/XnsNameCapture/XnsNameCaptureWithContext.tsx +20 -0
- package/src/components/XnsNameCapture/hooks/index.ts +2 -0
- package/src/components/XnsNameCapture/hooks/useXnsNameCaptureProviders.ts +13 -0
- package/src/components/XnsNameCapture/hooks/useXnsNameCaptureRouting.ts +18 -0
- package/src/components/XnsNameCapture/index.ts +6 -0
- package/src/components/index.ts +2 -0
- package/src/index.ts +1 -0
- package/typedoc.json +5 -0
- package/xy.config.ts +10 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Meta, StoryFn } from '@storybook/react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import type { To } from 'react-router-dom'
|
|
4
|
+
|
|
5
|
+
import { XnsNameCapture } from './XnsNameCapture.tsx'
|
|
6
|
+
|
|
7
|
+
export default { title: 'modules/xns/XnsNameCapture' } as Meta
|
|
8
|
+
|
|
9
|
+
const Template: StoryFn<typeof XnsNameCapture> = (args) => {
|
|
10
|
+
return <XnsNameCapture {...args}></XnsNameCapture>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const Default = Template.bind({})
|
|
14
|
+
Default.args = {}
|
|
15
|
+
|
|
16
|
+
const WithOnBuyName = Template.bind({})
|
|
17
|
+
WithOnBuyName.args = { navigate: (to: To) => alert(`navigated to: ${to}`), onBuyName: (name: string) => Promise.resolve(alert(`Buy Name: ${name}`)) }
|
|
18
|
+
|
|
19
|
+
export { Default, WithOnBuyName }
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { KeyboardArrowRightRounded } from '@mui/icons-material'
|
|
2
|
+
import type { StandardTextFieldProps } from '@mui/material'
|
|
3
|
+
import { useMediaQuery, useTheme } from '@mui/material'
|
|
4
|
+
import { ButtonEx } from '@xylabs/react-button'
|
|
5
|
+
import { FlexCol, FlexRow } from '@xylabs/react-flexbox'
|
|
6
|
+
import { MIN_DOMAIN_LENGTH, XnsNameHelper } from '@xyo-network/xns-record-payloadset-plugins'
|
|
7
|
+
import type { KeyboardEventHandler } from 'react'
|
|
8
|
+
import React, { useCallback, useState } from 'react'
|
|
9
|
+
|
|
10
|
+
import { XnsEstimateNameTextField } from '../EstimateName/index.ts'
|
|
11
|
+
import { XnsNameCaptureErrors } from './Errors.tsx'
|
|
12
|
+
import type { XnsNameCaptureProps } from './Props.ts'
|
|
13
|
+
import { XnsCaptureSecondaryLink } from './SecondaryLink.js'
|
|
14
|
+
|
|
15
|
+
export const XnsNameCapture: React.FC<XnsNameCaptureProps> = ({
|
|
16
|
+
autoFocus = false,
|
|
17
|
+
buttonText = 'Buy My Name',
|
|
18
|
+
children,
|
|
19
|
+
defaultXnsName,
|
|
20
|
+
errorUi = 'alert',
|
|
21
|
+
event = 'Click to Checkout',
|
|
22
|
+
funnel = 'xns',
|
|
23
|
+
mixpanel,
|
|
24
|
+
mobileButtonText = 'Buy',
|
|
25
|
+
navigate,
|
|
26
|
+
onBuyName: onBuyNameProp,
|
|
27
|
+
paramsString = '',
|
|
28
|
+
placement = '',
|
|
29
|
+
showSecondary = false,
|
|
30
|
+
to = '/xns/estimation',
|
|
31
|
+
userEvents,
|
|
32
|
+
...props
|
|
33
|
+
}) => {
|
|
34
|
+
const [xnsName, setXnsName] = useState<string>(() => defaultXnsName ?? '')
|
|
35
|
+
const [error, setError] = useState<Error | undefined>()
|
|
36
|
+
|
|
37
|
+
const theme = useTheme()
|
|
38
|
+
const isMobile = useMediaQuery(theme.breakpoints.down('md'))
|
|
39
|
+
|
|
40
|
+
const buyDisabled = !xnsName || xnsName.length < MIN_DOMAIN_LENGTH
|
|
41
|
+
|
|
42
|
+
const handleChange: StandardTextFieldProps['onChange'] = (event) => {
|
|
43
|
+
const NsName = XnsNameHelper.mask(event.target.value)
|
|
44
|
+
setXnsName(NsName)
|
|
45
|
+
setError(undefined)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const onBuyName = useCallback(async () => {
|
|
49
|
+
if (!xnsName) return
|
|
50
|
+
|
|
51
|
+
mixpanel?.track(event, {
|
|
52
|
+
Funnel: funnel,
|
|
53
|
+
Placement: placement,
|
|
54
|
+
})
|
|
55
|
+
const formattedXnsName = `${xnsName}.xyo`
|
|
56
|
+
const helper = XnsNameHelper.fromString(formattedXnsName)
|
|
57
|
+
const [valid, errors] = await helper.validate()
|
|
58
|
+
if (valid) {
|
|
59
|
+
await userEvents?.userClick({ elementName: event, elementType: 'xns-cta' })
|
|
60
|
+
await onBuyNameProp?.(xnsName)
|
|
61
|
+
navigate?.(`${to}?username=${xnsName}${paramsString}`)
|
|
62
|
+
} else {
|
|
63
|
+
setError(new Error(errors.join(', ')))
|
|
64
|
+
}
|
|
65
|
+
}, [event, funnel, mixpanel, paramsString, placement, to, userEvents, xnsName])
|
|
66
|
+
|
|
67
|
+
const onKeyDown: KeyboardEventHandler<HTMLDivElement> = useCallback(async (event) => {
|
|
68
|
+
if (event.key === 'Enter' && !buyDisabled) {
|
|
69
|
+
await onBuyName?.()
|
|
70
|
+
}
|
|
71
|
+
}, [buyDisabled, onBuyName])
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<FlexCol gap={showSecondary ? 1.5 : 0} alignItems="center" {...props}>
|
|
75
|
+
<FlexRow gap={1}>
|
|
76
|
+
<XnsEstimateNameTextField
|
|
77
|
+
autoFocus={autoFocus}
|
|
78
|
+
label="xNS Name"
|
|
79
|
+
variant="outlined"
|
|
80
|
+
size="small"
|
|
81
|
+
value={xnsName ?? ''}
|
|
82
|
+
onKeyDown={onKeyDown}
|
|
83
|
+
onChange={handleChange}
|
|
84
|
+
onBlur={handleChange}
|
|
85
|
+
/>
|
|
86
|
+
<ButtonEx
|
|
87
|
+
disabled={buyDisabled}
|
|
88
|
+
variant="contained"
|
|
89
|
+
color="success"
|
|
90
|
+
endIcon={<KeyboardArrowRightRounded />}
|
|
91
|
+
onClick={onBuyName}
|
|
92
|
+
>
|
|
93
|
+
{isMobile ? mobileButtonText : buttonText}
|
|
94
|
+
</ButtonEx>
|
|
95
|
+
</FlexRow>
|
|
96
|
+
{(showSecondary === true)
|
|
97
|
+
? (
|
|
98
|
+
<XnsCaptureSecondaryLink
|
|
99
|
+
xnsName={xnsName}
|
|
100
|
+
placement={placement}
|
|
101
|
+
funnel={funnel}
|
|
102
|
+
setError={setError}
|
|
103
|
+
/>
|
|
104
|
+
)
|
|
105
|
+
: null}
|
|
106
|
+
{
|
|
107
|
+
// eslint-disable-next-line unicorn/prefer-logical-operator-over-ternary
|
|
108
|
+
showSecondary ? showSecondary : null
|
|
109
|
+
}
|
|
110
|
+
{children}
|
|
111
|
+
<XnsNameCaptureErrors error={error} errorUi={errorUi} resetError={() => setError(undefined)} />
|
|
112
|
+
</FlexCol>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Meta, StoryFn } from '@storybook/react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import type { To } from 'react-router-dom'
|
|
4
|
+
import { BrowserRouter } from 'react-router-dom'
|
|
5
|
+
|
|
6
|
+
import { XnsNameCaptureWithContext } from './XnsNameCaptureWithContext.tsx'
|
|
7
|
+
|
|
8
|
+
export default { title: 'modules/xns/XnsNameCaptureWithContext' } as Meta
|
|
9
|
+
|
|
10
|
+
const Template: StoryFn<typeof XnsNameCaptureWithContext> = (args) => {
|
|
11
|
+
// Get the current URL
|
|
12
|
+
const url = new URL(window.location.href)
|
|
13
|
+
|
|
14
|
+
// Update or add the URL parameter
|
|
15
|
+
url.searchParams.set('signature', '0x1234567890abcdef')
|
|
16
|
+
|
|
17
|
+
// Update the URL without reloading the page
|
|
18
|
+
window.history.replaceState({}, '', url)
|
|
19
|
+
return (
|
|
20
|
+
<BrowserRouter>
|
|
21
|
+
<XnsNameCaptureWithContext {...args}></XnsNameCaptureWithContext>
|
|
22
|
+
</BrowserRouter>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const Default = Template.bind({})
|
|
27
|
+
Default.args = {}
|
|
28
|
+
|
|
29
|
+
const WithOnBuyName = Template.bind({})
|
|
30
|
+
WithOnBuyName.args = { navigate: (to: To) => alert(`navigated to: ${to}`), onBuyName: (name: string) => Promise.resolve(alert(`Buy Name: ${name}`)) }
|
|
31
|
+
|
|
32
|
+
export { Default, WithOnBuyName }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React, { useMemo } from 'react'
|
|
2
|
+
|
|
3
|
+
import { useXnsNameCaptureProviders, useXnsNameCaptureRouting } from './hooks/index.ts'
|
|
4
|
+
import type { XnsNameCaptureProps } from './Props.ts'
|
|
5
|
+
import { XnsNameCapture } from './XnsNameCapture.tsx'
|
|
6
|
+
|
|
7
|
+
export const XnsNameCaptureWithContext: React.FC<XnsNameCaptureProps> = (props) => {
|
|
8
|
+
const routingProps = useXnsNameCaptureRouting(props)
|
|
9
|
+
const providersProps = useXnsNameCaptureProviders(routingProps)
|
|
10
|
+
|
|
11
|
+
const updatedProps = useMemo<XnsNameCaptureProps>(() => ({
|
|
12
|
+
...props,
|
|
13
|
+
...routingProps,
|
|
14
|
+
...providersProps,
|
|
15
|
+
}), [providersProps])
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<XnsNameCapture {...updatedProps} />
|
|
19
|
+
)
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useMixpanel } from '@xylabs/react-mixpanel'
|
|
2
|
+
import { useMemo } from 'react'
|
|
3
|
+
|
|
4
|
+
import type { XnsNameCaptureProps } from '../Props.ts'
|
|
5
|
+
|
|
6
|
+
export const useXnsNameCaptureProviders = (props: XnsNameCaptureProps) => {
|
|
7
|
+
const mixpanel = useMixpanel()
|
|
8
|
+
|
|
9
|
+
return useMemo(() => ({
|
|
10
|
+
...props,
|
|
11
|
+
mixpanel: props.mixpanel ?? mixpanel,
|
|
12
|
+
}), [props, mixpanel])
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import { useNavigate, useSearchParams } from 'react-router-dom'
|
|
3
|
+
|
|
4
|
+
import type { XnsNameCaptureProps } from '../Props.ts'
|
|
5
|
+
|
|
6
|
+
export const useXnsNameCaptureRouting = (props: XnsNameCaptureProps) => {
|
|
7
|
+
const [params] = useSearchParams()
|
|
8
|
+
const signatureParam = params.get('signature')
|
|
9
|
+
const signatureParamString = signatureParam ? `&signature=${encodeURIComponent(signatureParam)}` : ''
|
|
10
|
+
|
|
11
|
+
const navigate = useNavigate()
|
|
12
|
+
|
|
13
|
+
return useMemo(() => ({
|
|
14
|
+
...props,
|
|
15
|
+
navigate: props.navigate ?? ((to: string) => navigate(to)),
|
|
16
|
+
paramsString: signatureParamString,
|
|
17
|
+
}), [props, signatureParamString])
|
|
18
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './components/index.ts'
|
package/typedoc.json
ADDED