@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.
Files changed (50) hide show
  1. package/LICENSE +165 -0
  2. package/README.md +13 -0
  3. package/dist/browser/components/EstimateName/EstimateNameTextField.d.ts +7 -0
  4. package/dist/browser/components/EstimateName/EstimateNameTextField.d.ts.map +1 -0
  5. package/dist/browser/components/EstimateName/index.d.ts +2 -0
  6. package/dist/browser/components/EstimateName/index.d.ts.map +1 -0
  7. package/dist/browser/components/XnsNameCapture/Errors.d.ts +8 -0
  8. package/dist/browser/components/XnsNameCapture/Errors.d.ts.map +1 -0
  9. package/dist/browser/components/XnsNameCapture/Props.d.ts +34 -0
  10. package/dist/browser/components/XnsNameCapture/Props.d.ts.map +1 -0
  11. package/dist/browser/components/XnsNameCapture/SecondaryLink.d.ts +14 -0
  12. package/dist/browser/components/XnsNameCapture/SecondaryLink.d.ts.map +1 -0
  13. package/dist/browser/components/XnsNameCapture/XnsNameCapture.d.ts +4 -0
  14. package/dist/browser/components/XnsNameCapture/XnsNameCapture.d.ts.map +1 -0
  15. package/dist/browser/components/XnsNameCapture/XnsNameCaptureWithContext.d.ts +4 -0
  16. package/dist/browser/components/XnsNameCapture/XnsNameCaptureWithContext.d.ts.map +1 -0
  17. package/dist/browser/components/XnsNameCapture/hooks/index.d.ts +3 -0
  18. package/dist/browser/components/XnsNameCapture/hooks/index.d.ts.map +1 -0
  19. package/dist/browser/components/XnsNameCapture/hooks/useXnsNameCaptureProviders.d.ts +399 -0
  20. package/dist/browser/components/XnsNameCapture/hooks/useXnsNameCaptureProviders.d.ts.map +1 -0
  21. package/dist/browser/components/XnsNameCapture/hooks/useXnsNameCaptureRouting.d.ts +399 -0
  22. package/dist/browser/components/XnsNameCapture/hooks/useXnsNameCaptureRouting.d.ts.map +1 -0
  23. package/dist/browser/components/XnsNameCapture/index.d.ts +7 -0
  24. package/dist/browser/components/XnsNameCapture/index.d.ts.map +1 -0
  25. package/dist/browser/components/index.d.ts +3 -0
  26. package/dist/browser/components/index.d.ts.map +1 -0
  27. package/dist/browser/index.d.ts +2 -0
  28. package/dist/browser/index.d.ts.map +1 -0
  29. package/dist/browser/index.mjs +276 -0
  30. package/dist/browser/index.mjs.map +1 -0
  31. package/package.json +85 -0
  32. package/src/components/EstimateName/EstimateNameTextField.stories.tsx +15 -0
  33. package/src/components/EstimateName/EstimateNameTextField.tsx +50 -0
  34. package/src/components/EstimateName/index.ts +1 -0
  35. package/src/components/XnsNameCapture/Errors.tsx +55 -0
  36. package/src/components/XnsNameCapture/Props.ts +50 -0
  37. package/src/components/XnsNameCapture/SecondaryLink.stories.tsx +18 -0
  38. package/src/components/XnsNameCapture/SecondaryLink.tsx +66 -0
  39. package/src/components/XnsNameCapture/XnsNameCapture.stories.tsx +19 -0
  40. package/src/components/XnsNameCapture/XnsNameCapture.tsx +114 -0
  41. package/src/components/XnsNameCapture/XnsNameCaptureWithContext.stories.tsx +32 -0
  42. package/src/components/XnsNameCapture/XnsNameCaptureWithContext.tsx +20 -0
  43. package/src/components/XnsNameCapture/hooks/index.ts +2 -0
  44. package/src/components/XnsNameCapture/hooks/useXnsNameCaptureProviders.ts +13 -0
  45. package/src/components/XnsNameCapture/hooks/useXnsNameCaptureRouting.ts +18 -0
  46. package/src/components/XnsNameCapture/index.ts +6 -0
  47. package/src/components/index.ts +2 -0
  48. package/src/index.ts +1 -0
  49. package/typedoc.json +5 -0
  50. package/xy.config.ts +10 -0
@@ -0,0 +1,276 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/components/EstimateName/EstimateNameTextField.tsx
5
+ import { alpha, TextField, useTheme } from "@mui/material";
6
+ import { MIN_DOMAIN_LENGTH, XnsNameHelper } from "@xyo-network/xns-record-payloadset-plugins";
7
+ import React, { useState } from "react";
8
+ var XnsEstimateNameTextField = /* @__PURE__ */ __name(({ maskOutput = true, onChange: onChangeProp, onBlur: onBlurProp, ...props }) => {
9
+ const theme = useTheme();
10
+ const [validLength, setValidLength] = useState(false);
11
+ const inputRef = React.useRef(null);
12
+ const handleChange = /* @__PURE__ */ __name((event) => {
13
+ if (maskOutput) {
14
+ const value = event.target.value;
15
+ event.target.value = XnsNameHelper.mask(value);
16
+ }
17
+ onChangeProp?.(event);
18
+ if (inputRef.current) {
19
+ setValidLength(inputRef.current.value.length >= MIN_DOMAIN_LENGTH);
20
+ }
21
+ }, "handleChange");
22
+ const handleBlur = /* @__PURE__ */ __name((event) => {
23
+ if (maskOutput) {
24
+ const value = event.target.value;
25
+ event.target.value = XnsNameHelper.mask(value, {
26
+ maskStartEndHyphens: true
27
+ });
28
+ }
29
+ onBlurProp?.(event);
30
+ }, "handleBlur");
31
+ return /* @__PURE__ */ React.createElement(TextField, {
32
+ inputProps: {
33
+ style: {
34
+ color: validLength ? theme.palette.text.primary : alpha(theme.palette.text.primary, 0.5)
35
+ }
36
+ },
37
+ inputRef,
38
+ onBlur: handleBlur,
39
+ onChange: handleChange,
40
+ ...props
41
+ });
42
+ }, "XnsEstimateNameTextField");
43
+
44
+ // src/components/XnsNameCapture/Errors.tsx
45
+ import { Alert, Snackbar, useMediaQuery, useTheme as useTheme2 } from "@mui/material";
46
+ import { FlexRow } from "@xylabs/react-flexbox";
47
+ import React2 from "react";
48
+ var XnsNameCaptureErrors = /* @__PURE__ */ __name(({ error, errorUi, resetError }) => {
49
+ const theme = useTheme2();
50
+ const isMobile = useMediaQuery(theme.breakpoints.down("md"));
51
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, errorUi === "toast" ? /* @__PURE__ */ React2.createElement(Snackbar, {
52
+ open: !!error,
53
+ message: error?.toString(),
54
+ autoHideDuration: 3e3,
55
+ onClose: /* @__PURE__ */ __name(() => resetError?.(), "onClose"),
56
+ anchorOrigin: {
57
+ vertical: "bottom",
58
+ horizontal: "center"
59
+ }
60
+ }, /* @__PURE__ */ React2.createElement(Alert, {
61
+ severity: "error",
62
+ sx: {
63
+ width: "100%",
64
+ display: isMobile && !error ? "none" : void 0,
65
+ visibility: error ? "visible" : "hidden"
66
+ }
67
+ }, error?.message)) : (() => {
68
+ return /* @__PURE__ */ React2.createElement(FlexRow, {
69
+ alignSelf: "stretch"
70
+ }, /* @__PURE__ */ React2.createElement(Alert, {
71
+ severity: "error",
72
+ sx: {
73
+ display: isMobile && !error ? "none" : void 0,
74
+ visibility: error ? "visible" : "hidden"
75
+ }
76
+ }, error?.message));
77
+ })());
78
+ }, "XnsNameCaptureErrors");
79
+
80
+ // src/components/XnsNameCapture/hooks/useXnsNameCaptureProviders.ts
81
+ import { useMixpanel } from "@xylabs/react-mixpanel";
82
+ import { useMemo } from "react";
83
+ var useXnsNameCaptureProviders = /* @__PURE__ */ __name((props) => {
84
+ const mixpanel = useMixpanel();
85
+ return useMemo(() => ({
86
+ ...props,
87
+ mixpanel: props.mixpanel ?? mixpanel
88
+ }), [
89
+ props,
90
+ mixpanel
91
+ ]);
92
+ }, "useXnsNameCaptureProviders");
93
+
94
+ // src/components/XnsNameCapture/hooks/useXnsNameCaptureRouting.ts
95
+ import { useMemo as useMemo2 } from "react";
96
+ import { useNavigate, useSearchParams } from "react-router-dom";
97
+ var useXnsNameCaptureRouting = /* @__PURE__ */ __name((props) => {
98
+ const [params] = useSearchParams();
99
+ const signatureParam = params.get("signature");
100
+ const signatureParamString = signatureParam ? `&signature=${encodeURIComponent(signatureParam)}` : "";
101
+ const navigate = useNavigate();
102
+ return useMemo2(() => ({
103
+ ...props,
104
+ navigate: props.navigate ?? ((to) => navigate(to)),
105
+ paramsString: signatureParamString
106
+ }), [
107
+ props,
108
+ signatureParamString
109
+ ]);
110
+ }, "useXnsNameCaptureRouting");
111
+
112
+ // src/components/XnsNameCapture/SecondaryLink.tsx
113
+ import { ArrowForwardRounded } from "@mui/icons-material";
114
+ import { Stack } from "@mui/material";
115
+ import { LinkEx } from "@xylabs/react-link";
116
+ import { XnsNameHelper as XnsNameHelper2 } from "@xyo-network/xns-record-payloadset-plugins";
117
+ import React3 from "react";
118
+ var XnsCaptureSecondaryLink = /* @__PURE__ */ __name(({ event = "Click to Reservation", funnel = "xns", mixpanel, navigate, onBuyName, paramsString = "", placement = "", setError, text = "Or make a free reservation", to = "/xns/reservation", userEvents, xnsName, ...props }) => {
119
+ return /* @__PURE__ */ React3.createElement(LinkEx, {
120
+ paddingX: 0,
121
+ color: "inherit",
122
+ style: {
123
+ textDecoration: "underline",
124
+ textUnderlineOffset: "5px"
125
+ },
126
+ onClick: /* @__PURE__ */ __name(async () => {
127
+ mixpanel?.track(event, {
128
+ Funnel: funnel,
129
+ Placement: placement
130
+ });
131
+ const formattedXnsName = `${xnsName}.xyo`;
132
+ const helper = XnsNameHelper2.fromString(formattedXnsName);
133
+ const [valid, errors] = await helper.validate();
134
+ if (valid) {
135
+ await userEvents?.userClick({
136
+ elementName: event,
137
+ elementType: "xns-cta"
138
+ });
139
+ navigate?.(`${to}?username=${xnsName}${paramsString}`);
140
+ await onBuyName?.(xnsName);
141
+ } else {
142
+ setError?.(new Error(errors.join(", ")));
143
+ }
144
+ }, "onClick"),
145
+ ...props
146
+ }, /* @__PURE__ */ React3.createElement(Stack, {
147
+ flexDirection: "row",
148
+ gap: 0.5,
149
+ alignItems: "center",
150
+ sx: {
151
+ cursor: "pointer"
152
+ }
153
+ }, text, /* @__PURE__ */ React3.createElement(ArrowForwardRounded, null)));
154
+ }, "XnsCaptureSecondaryLink");
155
+
156
+ // src/components/XnsNameCapture/XnsNameCapture.tsx
157
+ import { KeyboardArrowRightRounded } from "@mui/icons-material";
158
+ import { useMediaQuery as useMediaQuery2, useTheme as useTheme3 } from "@mui/material";
159
+ import { ButtonEx } from "@xylabs/react-button";
160
+ import { FlexCol, FlexRow as FlexRow2 } from "@xylabs/react-flexbox";
161
+ import { MIN_DOMAIN_LENGTH as MIN_DOMAIN_LENGTH2, XnsNameHelper as XnsNameHelper3 } from "@xyo-network/xns-record-payloadset-plugins";
162
+ import React4, { useCallback, useState as useState2 } from "react";
163
+ var XnsNameCapture = /* @__PURE__ */ __name(({ autoFocus = false, buttonText = "Buy My Name", children, defaultXnsName, errorUi = "alert", event = "Click to Checkout", funnel = "xns", mixpanel, mobileButtonText = "Buy", navigate, onBuyName: onBuyNameProp, paramsString = "", placement = "", showSecondary = false, to = "/xns/estimation", userEvents, ...props }) => {
164
+ const [xnsName, setXnsName] = useState2(() => defaultXnsName ?? "");
165
+ const [error, setError] = useState2();
166
+ const theme = useTheme3();
167
+ const isMobile = useMediaQuery2(theme.breakpoints.down("md"));
168
+ const buyDisabled = !xnsName || xnsName.length < MIN_DOMAIN_LENGTH2;
169
+ const handleChange = /* @__PURE__ */ __name((event2) => {
170
+ const NsName = XnsNameHelper3.mask(event2.target.value);
171
+ setXnsName(NsName);
172
+ setError(void 0);
173
+ }, "handleChange");
174
+ const onBuyName = useCallback(async () => {
175
+ if (!xnsName) return;
176
+ mixpanel?.track(event, {
177
+ Funnel: funnel,
178
+ Placement: placement
179
+ });
180
+ const formattedXnsName = `${xnsName}.xyo`;
181
+ const helper = XnsNameHelper3.fromString(formattedXnsName);
182
+ const [valid, errors] = await helper.validate();
183
+ if (valid) {
184
+ await userEvents?.userClick({
185
+ elementName: event,
186
+ elementType: "xns-cta"
187
+ });
188
+ await onBuyNameProp?.(xnsName);
189
+ navigate?.(`${to}?username=${xnsName}${paramsString}`);
190
+ } else {
191
+ setError(new Error(errors.join(", ")));
192
+ }
193
+ }, [
194
+ event,
195
+ funnel,
196
+ mixpanel,
197
+ paramsString,
198
+ placement,
199
+ to,
200
+ userEvents,
201
+ xnsName
202
+ ]);
203
+ const onKeyDown = useCallback(async (event2) => {
204
+ if (event2.key === "Enter" && !buyDisabled) {
205
+ await onBuyName?.();
206
+ }
207
+ }, [
208
+ buyDisabled,
209
+ onBuyName
210
+ ]);
211
+ return /* @__PURE__ */ React4.createElement(
212
+ FlexCol,
213
+ {
214
+ gap: showSecondary ? 1.5 : 0,
215
+ alignItems: "center",
216
+ ...props
217
+ },
218
+ /* @__PURE__ */ React4.createElement(FlexRow2, {
219
+ gap: 1
220
+ }, /* @__PURE__ */ React4.createElement(XnsEstimateNameTextField, {
221
+ autoFocus,
222
+ label: "xNS Name",
223
+ variant: "outlined",
224
+ size: "small",
225
+ value: xnsName ?? "",
226
+ onKeyDown,
227
+ onChange: handleChange,
228
+ onBlur: handleChange
229
+ }), /* @__PURE__ */ React4.createElement(ButtonEx, {
230
+ disabled: buyDisabled,
231
+ variant: "contained",
232
+ color: "success",
233
+ endIcon: /* @__PURE__ */ React4.createElement(KeyboardArrowRightRounded, null),
234
+ onClick: onBuyName
235
+ }, isMobile ? mobileButtonText : buttonText)),
236
+ showSecondary === true ? /* @__PURE__ */ React4.createElement(XnsCaptureSecondaryLink, {
237
+ xnsName,
238
+ placement,
239
+ funnel,
240
+ setError
241
+ }) : null,
242
+ // eslint-disable-next-line unicorn/prefer-logical-operator-over-ternary
243
+ showSecondary ? showSecondary : null,
244
+ children,
245
+ /* @__PURE__ */ React4.createElement(XnsNameCaptureErrors, {
246
+ error,
247
+ errorUi,
248
+ resetError: /* @__PURE__ */ __name(() => setError(void 0), "resetError")
249
+ })
250
+ );
251
+ }, "XnsNameCapture");
252
+
253
+ // src/components/XnsNameCapture/XnsNameCaptureWithContext.tsx
254
+ import React5, { useMemo as useMemo3 } from "react";
255
+ var XnsNameCaptureWithContext = /* @__PURE__ */ __name((props) => {
256
+ const routingProps = useXnsNameCaptureRouting(props);
257
+ const providersProps = useXnsNameCaptureProviders(routingProps);
258
+ const updatedProps = useMemo3(() => ({
259
+ ...props,
260
+ ...routingProps,
261
+ ...providersProps
262
+ }), [
263
+ providersProps
264
+ ]);
265
+ return /* @__PURE__ */ React5.createElement(XnsNameCapture, updatedProps);
266
+ }, "XnsNameCaptureWithContext");
267
+ export {
268
+ XnsCaptureSecondaryLink,
269
+ XnsEstimateNameTextField,
270
+ XnsNameCapture,
271
+ XnsNameCaptureErrors,
272
+ XnsNameCaptureWithContext,
273
+ useXnsNameCaptureProviders,
274
+ useXnsNameCaptureRouting
275
+ };
276
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/EstimateName/EstimateNameTextField.tsx","../../src/components/XnsNameCapture/Errors.tsx","../../src/components/XnsNameCapture/hooks/useXnsNameCaptureProviders.ts","../../src/components/XnsNameCapture/hooks/useXnsNameCaptureRouting.ts","../../src/components/XnsNameCapture/SecondaryLink.tsx","../../src/components/XnsNameCapture/XnsNameCapture.tsx","../../src/components/XnsNameCapture/XnsNameCaptureWithContext.tsx"],"sourcesContent":["import type { StandardTextFieldProps, TextFieldProps } from '@mui/material'\nimport {\n alpha, TextField, useTheme,\n} from '@mui/material'\nimport { MIN_DOMAIN_LENGTH, XnsNameHelper } from '@xyo-network/xns-record-payloadset-plugins'\nimport React, { useState } from 'react'\n\nexport interface XnsEstimateNameTextFieldProps {\n maskOutput?: boolean\n}\n\nexport const XnsEstimateNameTextField: React.FC<XnsEstimateNameTextFieldProps & TextFieldProps> = ({\n maskOutput = true, onChange: onChangeProp, onBlur: onBlurProp, ...props\n}) => {\n const theme = useTheme()\n const [validLength, setValidLength] = useState(false)\n\n const inputRef = React.useRef<HTMLInputElement>(null)\n\n // override onChange to mask the input and update the event value\n const handleChange: StandardTextFieldProps['onChange'] = (event) => {\n if (maskOutput) {\n const value = event.target.value\n event.target.value = XnsNameHelper.mask(value)\n }\n onChangeProp?.(event)\n\n if (inputRef.current) {\n setValidLength(inputRef.current.value.length >= MIN_DOMAIN_LENGTH)\n }\n }\n\n const handleBlur: StandardTextFieldProps['onBlur'] = (event) => {\n if (maskOutput) {\n const value = event.target.value\n event.target.value = XnsNameHelper.mask(value, { maskStartEndHyphens: true })\n }\n onBlurProp?.(event)\n }\n\n return (\n <TextField\n inputProps={{ style: { color: validLength ? theme.palette.text.primary : alpha(theme.palette.text.primary, 0.5) } }}\n inputRef={inputRef}\n onBlur={handleBlur}\n onChange={handleChange}\n {...props}\n />\n )\n}\n","import {\n Alert, Snackbar, useMediaQuery, useTheme,\n} from '@mui/material'\nimport { FlexRow } from '@xylabs/react-flexbox'\nimport React from 'react'\n\nexport interface XnsNameCaptureErrorsProps {\n error?: Error\n errorUi?: 'alert' | 'toast'\n resetError?: () => void\n}\n\nexport const XnsNameCaptureErrors: React.FC<XnsNameCaptureErrorsProps> = ({\n error, errorUi, resetError,\n}) => {\n const theme = useTheme()\n const isMobile = useMediaQuery(theme.breakpoints.down('md'))\n\n return (\n <>\n {(errorUi === 'toast')\n ? (\n <Snackbar\n open={!!error}\n message={error?.toString()}\n autoHideDuration={3000}\n onClose={() => resetError?.()}\n anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}\n >\n <Alert\n severity=\"error\"\n sx={{\n width: '100%', display: (isMobile && !error) ? 'none' : undefined, visibility: error ? 'visible' : 'hidden',\n }}\n >\n {error?.message}\n </Alert>\n </Snackbar>\n )\n : (() => {\n // setTimeout(() => setError(undefined), 1500)\n return (\n <FlexRow alignSelf=\"stretch\">\n <Alert\n severity=\"error\"\n sx={{ display: (isMobile && !error) ? 'none' : undefined, visibility: error ? 'visible' : 'hidden' }}\n >\n {error?.message}\n </Alert>\n </FlexRow>\n )\n })()}\n </>\n )\n}\n","import { useMixpanel } from '@xylabs/react-mixpanel'\nimport { useMemo } from 'react'\n\nimport type { XnsNameCaptureProps } from '../Props.ts'\n\nexport const useXnsNameCaptureProviders = (props: XnsNameCaptureProps) => {\n const mixpanel = useMixpanel()\n\n return useMemo(() => ({\n ...props,\n mixpanel: props.mixpanel ?? mixpanel,\n }), [props, mixpanel])\n}\n","import { useMemo } from 'react'\nimport { useNavigate, useSearchParams } from 'react-router-dom'\n\nimport type { XnsNameCaptureProps } from '../Props.ts'\n\nexport const useXnsNameCaptureRouting = (props: XnsNameCaptureProps) => {\n const [params] = useSearchParams()\n const signatureParam = params.get('signature')\n const signatureParamString = signatureParam ? `&signature=${encodeURIComponent(signatureParam)}` : ''\n\n const navigate = useNavigate()\n\n return useMemo(() => ({\n ...props,\n navigate: props.navigate ?? ((to: string) => navigate(to)),\n paramsString: signatureParamString,\n }), [props, signatureParamString])\n}\n","import { ArrowForwardRounded } from '@mui/icons-material'\nimport { Stack } from '@mui/material'\nimport type { LinkExProps } from '@xylabs/react-link'\nimport { LinkEx } from '@xylabs/react-link'\nimport { XnsNameHelper } from '@xyo-network/xns-record-payloadset-plugins'\nimport type { Dispatch } from 'react'\nimport React from 'react'\n\nimport type {\n XnsNameCaptureBuyCallbacks, XnsNameCaptureRoutingProps, XnsNameCaptureTrackingProps,\n} from './Props.ts'\n\nexport interface XnsCaptureSecondaryLinkProps extends XnsNameCaptureTrackingProps, XnsNameCaptureRoutingProps, XnsNameCaptureBuyCallbacks, LinkExProps {\n event?: string\n funnel?: string\n placement?: string\n setError?: Dispatch<Error | undefined>\n text?: string\n xnsName: string\n}\n\nexport const XnsCaptureSecondaryLink: React.FC<XnsCaptureSecondaryLinkProps> = ({\n event = 'Click to Reservation',\n funnel = 'xns',\n mixpanel,\n navigate,\n onBuyName,\n paramsString = '',\n placement = '',\n setError,\n text = 'Or make a free reservation',\n to = '/xns/reservation',\n userEvents,\n xnsName,\n ...props\n}) => {\n return (\n <LinkEx\n paddingX={0}\n color=\"inherit\"\n style={{ textDecoration: 'underline', textUnderlineOffset: '5px' }}\n onClick={async () => {\n mixpanel?.track(event, {\n Funnel: funnel,\n Placement: placement,\n })\n const formattedXnsName = `${xnsName}.xyo`\n const helper = XnsNameHelper.fromString(formattedXnsName)\n const [valid, errors] = await helper.validate()\n if (valid) {\n await userEvents?.userClick({ elementName: event, elementType: 'xns-cta' })\n navigate?.(`${to}?username=${xnsName}${paramsString}`)\n await onBuyName?.(xnsName)\n } else {\n setError?.(new Error(errors.join(', ')))\n }\n }}\n {...props}\n >\n <Stack flexDirection=\"row\" gap={0.5} alignItems=\"center\" sx={{ cursor: 'pointer' }}>\n {text}\n <ArrowForwardRounded />\n </Stack>\n </LinkEx>\n )\n}\n","import { KeyboardArrowRightRounded } from '@mui/icons-material'\nimport type { StandardTextFieldProps } from '@mui/material'\nimport { useMediaQuery, useTheme } from '@mui/material'\nimport { ButtonEx } from '@xylabs/react-button'\nimport { FlexCol, FlexRow } from '@xylabs/react-flexbox'\nimport { MIN_DOMAIN_LENGTH, XnsNameHelper } from '@xyo-network/xns-record-payloadset-plugins'\nimport type { KeyboardEventHandler } from 'react'\nimport React, { useCallback, useState } from 'react'\n\nimport { XnsEstimateNameTextField } from '../EstimateName/index.ts'\nimport { XnsNameCaptureErrors } from './Errors.tsx'\nimport type { XnsNameCaptureProps } from './Props.ts'\nimport { XnsCaptureSecondaryLink } from './SecondaryLink.js'\n\nexport const XnsNameCapture: React.FC<XnsNameCaptureProps> = ({\n autoFocus = false,\n buttonText = 'Buy My Name',\n children,\n defaultXnsName,\n errorUi = 'alert',\n event = 'Click to Checkout',\n funnel = 'xns',\n mixpanel,\n mobileButtonText = 'Buy',\n navigate,\n onBuyName: onBuyNameProp,\n paramsString = '',\n placement = '',\n showSecondary = false,\n to = '/xns/estimation',\n userEvents,\n ...props\n}) => {\n const [xnsName, setXnsName] = useState<string>(() => defaultXnsName ?? '')\n const [error, setError] = useState<Error | undefined>()\n\n const theme = useTheme()\n const isMobile = useMediaQuery(theme.breakpoints.down('md'))\n\n const buyDisabled = !xnsName || xnsName.length < MIN_DOMAIN_LENGTH\n\n const handleChange: StandardTextFieldProps['onChange'] = (event) => {\n const NsName = XnsNameHelper.mask(event.target.value)\n setXnsName(NsName)\n setError(undefined)\n }\n\n const onBuyName = useCallback(async () => {\n if (!xnsName) return\n\n mixpanel?.track(event, {\n Funnel: funnel,\n Placement: placement,\n })\n const formattedXnsName = `${xnsName}.xyo`\n const helper = XnsNameHelper.fromString(formattedXnsName)\n const [valid, errors] = await helper.validate()\n if (valid) {\n await userEvents?.userClick({ elementName: event, elementType: 'xns-cta' })\n await onBuyNameProp?.(xnsName)\n navigate?.(`${to}?username=${xnsName}${paramsString}`)\n } else {\n setError(new Error(errors.join(', ')))\n }\n }, [event, funnel, mixpanel, paramsString, placement, to, userEvents, xnsName])\n\n const onKeyDown: KeyboardEventHandler<HTMLDivElement> = useCallback(async (event) => {\n if (event.key === 'Enter' && !buyDisabled) {\n await onBuyName?.()\n }\n }, [buyDisabled, onBuyName])\n\n return (\n <FlexCol gap={showSecondary ? 1.5 : 0} alignItems=\"center\" {...props}>\n <FlexRow gap={1}>\n <XnsEstimateNameTextField\n autoFocus={autoFocus}\n label=\"xNS Name\"\n variant=\"outlined\"\n size=\"small\"\n value={xnsName ?? ''}\n onKeyDown={onKeyDown}\n onChange={handleChange}\n onBlur={handleChange}\n />\n <ButtonEx\n disabled={buyDisabled}\n variant=\"contained\"\n color=\"success\"\n endIcon={<KeyboardArrowRightRounded />}\n onClick={onBuyName}\n >\n {isMobile ? mobileButtonText : buttonText}\n </ButtonEx>\n </FlexRow>\n {(showSecondary === true)\n ? (\n <XnsCaptureSecondaryLink\n xnsName={xnsName}\n placement={placement}\n funnel={funnel}\n setError={setError}\n />\n )\n : null}\n {\n // eslint-disable-next-line unicorn/prefer-logical-operator-over-ternary\n showSecondary ? showSecondary : null\n }\n {children}\n <XnsNameCaptureErrors error={error} errorUi={errorUi} resetError={() => setError(undefined)} />\n </FlexCol>\n )\n}\n","import React, { useMemo } from 'react'\n\nimport { useXnsNameCaptureProviders, useXnsNameCaptureRouting } from './hooks/index.ts'\nimport type { XnsNameCaptureProps } from './Props.ts'\nimport { XnsNameCapture } from './XnsNameCapture.tsx'\n\nexport const XnsNameCaptureWithContext: React.FC<XnsNameCaptureProps> = (props) => {\n const routingProps = useXnsNameCaptureRouting(props)\n const providersProps = useXnsNameCaptureProviders(routingProps)\n\n const updatedProps = useMemo<XnsNameCaptureProps>(() => ({\n ...props,\n ...routingProps,\n ...providersProps,\n }), [providersProps])\n\n return (\n <XnsNameCapture {...updatedProps} />\n )\n}\n"],"mappings":"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","names":["alpha","TextField","useTheme","MIN_DOMAIN_LENGTH","XnsNameHelper","React","useState","XnsEstimateNameTextField","maskOutput","onChange","onChangeProp","onBlur","onBlurProp","props","theme","useTheme","validLength","setValidLength","useState","inputRef","React","useRef","handleChange","event","value","target","XnsNameHelper","mask","current","length","MIN_DOMAIN_LENGTH","handleBlur","maskStartEndHyphens","TextField","inputProps","style","color","palette","text","primary","alpha","Alert","Snackbar","useMediaQuery","useTheme","FlexRow","React","XnsNameCaptureErrors","error","errorUi","resetError","theme","useTheme","isMobile","useMediaQuery","breakpoints","down","React","Snackbar","open","message","toString","autoHideDuration","onClose","anchorOrigin","vertical","horizontal","Alert","severity","sx","width","display","undefined","visibility","FlexRow","alignSelf","useMixpanel","useMemo","useXnsNameCaptureProviders","props","mixpanel","useMixpanel","useMemo","useMemo","useNavigate","useSearchParams","useXnsNameCaptureRouting","props","params","useSearchParams","signatureParam","get","signatureParamString","encodeURIComponent","navigate","useNavigate","useMemo","to","paramsString","ArrowForwardRounded","Stack","LinkEx","XnsNameHelper","React","XnsCaptureSecondaryLink","event","funnel","mixpanel","navigate","onBuyName","paramsString","placement","setError","text","to","userEvents","xnsName","props","React","LinkEx","paddingX","color","style","textDecoration","textUnderlineOffset","onClick","track","Funnel","Placement","formattedXnsName","helper","XnsNameHelper","fromString","valid","errors","validate","userClick","elementName","elementType","Error","join","Stack","flexDirection","gap","alignItems","sx","cursor","ArrowForwardRounded","KeyboardArrowRightRounded","useMediaQuery","useTheme","ButtonEx","FlexCol","FlexRow","MIN_DOMAIN_LENGTH","XnsNameHelper","React","useCallback","useState","XnsNameCapture","autoFocus","buttonText","children","defaultXnsName","errorUi","event","funnel","mixpanel","mobileButtonText","navigate","onBuyName","onBuyNameProp","paramsString","placement","showSecondary","to","userEvents","props","xnsName","setXnsName","useState","error","setError","theme","useTheme","isMobile","useMediaQuery","breakpoints","down","buyDisabled","length","MIN_DOMAIN_LENGTH","handleChange","NsName","XnsNameHelper","mask","target","value","undefined","useCallback","track","Funnel","Placement","formattedXnsName","helper","fromString","valid","errors","validate","userClick","elementName","elementType","Error","join","onKeyDown","key","React","FlexCol","gap","alignItems","FlexRow","XnsEstimateNameTextField","label","variant","size","onChange","onBlur","ButtonEx","disabled","color","endIcon","KeyboardArrowRightRounded","onClick","XnsCaptureSecondaryLink","XnsNameCaptureErrors","resetError","React","useMemo","XnsNameCaptureWithContext","props","routingProps","useXnsNameCaptureRouting","providersProps","useXnsNameCaptureProviders","updatedProps","useMemo","React","XnsNameCapture"]}
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@xyo-network/react-xns",
3
+ "version": "3.0.10",
4
+ "description": "Common React library for all XYO projects that use React",
5
+ "keywords": [
6
+ "xyo",
7
+ "utility",
8
+ "typescript",
9
+ "react"
10
+ ],
11
+ "homepage": "https://xyo.network",
12
+ "bugs": {
13
+ "url": "git+https://github.com/XYOracleNetwork/sdk-xyo-react-js/issues",
14
+ "email": "support@xyo.network"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/XYOracleNetwork/sdk-xyo-react-js.git"
19
+ },
20
+ "license": "LGPL-3.0-only",
21
+ "author": {
22
+ "name": "XYO Development Team",
23
+ "email": "support@xyo.network",
24
+ "url": "https://xyo.network"
25
+ },
26
+ "sideEffects": false,
27
+ "type": "module",
28
+ "exports": {
29
+ ".": {
30
+ "import": {
31
+ "types": "./dist/browser/index.d.ts",
32
+ "default": "./dist/browser/index.mjs"
33
+ },
34
+ "types": "./dist/browser/index.d.ts",
35
+ "default": "./dist/browser/index.mjs"
36
+ },
37
+ "./package.json": "./package.json"
38
+ },
39
+ "module": "dist/browser/index.mjs",
40
+ "types": "dist/browser/index.d.ts",
41
+ "scripts": {
42
+ "license": "yarn license-checker --exclude \"MIT, ISC, Apache-2.0, BSD, BSD-2-Clause, CC-BY-4.0, Unlicense, CC-BY-3.0, CC0-1.0\"",
43
+ "lint-pkg": "npmPkgJsonLint ."
44
+ },
45
+ "dependencies": {
46
+ "@xylabs/pixel": "^2.0.1",
47
+ "@xylabs/react-button": "^4.2.5",
48
+ "@xylabs/react-flexbox": "^4.2.5",
49
+ "@xylabs/react-link": "^4.2.5",
50
+ "@xylabs/react-mixpanel": "^4.2.5",
51
+ "@xyo-network/xns-record-payloadset-plugins": "^3.0.17",
52
+ "mixpanel-browser": "^2.55.1"
53
+ },
54
+ "devDependencies": {
55
+ "@mui/icons-material": "^5.16.7",
56
+ "@mui/material": "^5.16.7",
57
+ "@mui/styles": "^5.16.7",
58
+ "@storybook/react": "^8.2.9",
59
+ "@types/mixpanel-browser": "^2.50.0",
60
+ "@xylabs/ts-scripts-yarn3": "^4.0.7",
61
+ "@xylabs/tsconfig-react": "^4.0.7",
62
+ "@xyo-network/react-storybook": "^3.0.10",
63
+ "react": "^18.3.1",
64
+ "react-dom": "^18.3.1",
65
+ "react-router-dom": "^6.26.1",
66
+ "storybook": "^8.2.9",
67
+ "typescript": "^5.5.4"
68
+ },
69
+ "peerDependencies": {
70
+ "@emotion/react": "^11",
71
+ "@emotion/styled": "^11",
72
+ "@mui/icons-material": "^5",
73
+ "@mui/material": "^5",
74
+ "@mui/styles": "^5",
75
+ "@xylabs/pixel": "^2",
76
+ "react": "^18",
77
+ "react-dom": "^18",
78
+ "react-helmet": "^6",
79
+ "react-router-dom": "^6"
80
+ },
81
+ "publishConfig": {
82
+ "access": "public"
83
+ },
84
+ "docs": "dist/docs.json"
85
+ }
@@ -0,0 +1,15 @@
1
+ import type { Meta, StoryFn } from '@storybook/react'
2
+ import React from 'react'
3
+
4
+ import { XnsEstimateNameTextField } from './EstimateNameTextField.tsx'
5
+
6
+ export default { title: 'modules/xns/XnsNameEstimateTextField' } as Meta
7
+
8
+ const Template: StoryFn<typeof XnsEstimateNameTextField> = (args) => {
9
+ return <XnsEstimateNameTextField {...args}></XnsEstimateNameTextField>
10
+ }
11
+
12
+ const Default = Template.bind({})
13
+ Default.args = {}
14
+
15
+ export { Default }
@@ -0,0 +1,50 @@
1
+ import type { StandardTextFieldProps, TextFieldProps } from '@mui/material'
2
+ import {
3
+ alpha, TextField, useTheme,
4
+ } from '@mui/material'
5
+ import { MIN_DOMAIN_LENGTH, XnsNameHelper } from '@xyo-network/xns-record-payloadset-plugins'
6
+ import React, { useState } from 'react'
7
+
8
+ export interface XnsEstimateNameTextFieldProps {
9
+ maskOutput?: boolean
10
+ }
11
+
12
+ export const XnsEstimateNameTextField: React.FC<XnsEstimateNameTextFieldProps & TextFieldProps> = ({
13
+ maskOutput = true, onChange: onChangeProp, onBlur: onBlurProp, ...props
14
+ }) => {
15
+ const theme = useTheme()
16
+ const [validLength, setValidLength] = useState(false)
17
+
18
+ const inputRef = React.useRef<HTMLInputElement>(null)
19
+
20
+ // override onChange to mask the input and update the event value
21
+ const handleChange: StandardTextFieldProps['onChange'] = (event) => {
22
+ if (maskOutput) {
23
+ const value = event.target.value
24
+ event.target.value = XnsNameHelper.mask(value)
25
+ }
26
+ onChangeProp?.(event)
27
+
28
+ if (inputRef.current) {
29
+ setValidLength(inputRef.current.value.length >= MIN_DOMAIN_LENGTH)
30
+ }
31
+ }
32
+
33
+ const handleBlur: StandardTextFieldProps['onBlur'] = (event) => {
34
+ if (maskOutput) {
35
+ const value = event.target.value
36
+ event.target.value = XnsNameHelper.mask(value, { maskStartEndHyphens: true })
37
+ }
38
+ onBlurProp?.(event)
39
+ }
40
+
41
+ return (
42
+ <TextField
43
+ inputProps={{ style: { color: validLength ? theme.palette.text.primary : alpha(theme.palette.text.primary, 0.5) } }}
44
+ inputRef={inputRef}
45
+ onBlur={handleBlur}
46
+ onChange={handleChange}
47
+ {...props}
48
+ />
49
+ )
50
+ }
@@ -0,0 +1 @@
1
+ export * from './EstimateNameTextField.tsx'
@@ -0,0 +1,55 @@
1
+ import {
2
+ Alert, Snackbar, useMediaQuery, useTheme,
3
+ } from '@mui/material'
4
+ import { FlexRow } from '@xylabs/react-flexbox'
5
+ import React from 'react'
6
+
7
+ export interface XnsNameCaptureErrorsProps {
8
+ error?: Error
9
+ errorUi?: 'alert' | 'toast'
10
+ resetError?: () => void
11
+ }
12
+
13
+ export const XnsNameCaptureErrors: React.FC<XnsNameCaptureErrorsProps> = ({
14
+ error, errorUi, resetError,
15
+ }) => {
16
+ const theme = useTheme()
17
+ const isMobile = useMediaQuery(theme.breakpoints.down('md'))
18
+
19
+ return (
20
+ <>
21
+ {(errorUi === 'toast')
22
+ ? (
23
+ <Snackbar
24
+ open={!!error}
25
+ message={error?.toString()}
26
+ autoHideDuration={3000}
27
+ onClose={() => resetError?.()}
28
+ anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
29
+ >
30
+ <Alert
31
+ severity="error"
32
+ sx={{
33
+ width: '100%', display: (isMobile && !error) ? 'none' : undefined, visibility: error ? 'visible' : 'hidden',
34
+ }}
35
+ >
36
+ {error?.message}
37
+ </Alert>
38
+ </Snackbar>
39
+ )
40
+ : (() => {
41
+ // setTimeout(() => setError(undefined), 1500)
42
+ return (
43
+ <FlexRow alignSelf="stretch">
44
+ <Alert
45
+ severity="error"
46
+ sx={{ display: (isMobile && !error) ? 'none' : undefined, visibility: error ? 'visible' : 'hidden' }}
47
+ >
48
+ {error?.message}
49
+ </Alert>
50
+ </FlexRow>
51
+ )
52
+ })()}
53
+ </>
54
+ )
55
+ }
@@ -0,0 +1,50 @@
1
+ import type { UserEventHandler } from '@xylabs/pixel'
2
+ import type { FlexBoxProps } from '@xylabs/react-flexbox'
3
+ import type { Mixpanel } from 'mixpanel-browser'
4
+ import type { ReactNode } from 'react'
5
+ import type { To } from 'react-router-dom'
6
+
7
+ export interface XnsNameCaptureBuyCallbacks {
8
+ onBuyName?: (name: string) => Promise<void>
9
+ }
10
+
11
+ /**
12
+ * Trackers for user actions
13
+ */
14
+ export interface XnsNameCaptureTrackingProps {
15
+ event?: string
16
+ funnel?: string
17
+ mixpanel?: Mixpanel
18
+ userEvents?: UserEventHandler<Record<string, unknown>>
19
+ }
20
+
21
+ /**
22
+ * Properties derived from the route and used for navigation
23
+ */
24
+ export interface XnsNameCaptureRoutingProps {
25
+ navigate?: (to: string) => void
26
+ paramsString?: string
27
+ to?: To
28
+ }
29
+
30
+ /**
31
+ * Base properties for the XnsNameCapture component related to the UI and Events
32
+ */
33
+ export interface XnsNameCaptureBaseProps {
34
+ autoFocus?: boolean
35
+ buttonText?: string
36
+ defaultXnsName?: string
37
+ errorUi?: 'alert' | 'toast'
38
+ mobileButtonText?: string
39
+ placement?: string
40
+ showSecondary?: boolean | ReactNode
41
+ }
42
+
43
+ export interface XnsNameCaptureProps extends XnsNameCaptureBaseProps,
44
+ XnsNameCaptureTrackingProps,
45
+ XnsNameCaptureBuyCallbacks,
46
+ XnsNameCaptureRoutingProps,
47
+ FlexBoxProps
48
+ {}
49
+
50
+ export type WithXnsCapture<T> = T & { XnsCapture?: React.FC<XnsNameCaptureProps> }
@@ -0,0 +1,18 @@
1
+ import type { Meta, StoryFn } from '@storybook/react'
2
+ import React from 'react'
3
+
4
+ import { XnsCaptureSecondaryLink } from './SecondaryLink.tsx'
5
+
6
+ export default { title: 'modules/xns/XnsCaptureSecondaryLink' } as Meta
7
+
8
+ const Template: StoryFn<typeof XnsCaptureSecondaryLink> = (args) => {
9
+ return <XnsCaptureSecondaryLink {...args}></XnsCaptureSecondaryLink>
10
+ }
11
+
12
+ const Default = Template.bind({})
13
+ Default.args = {}
14
+
15
+ const WithOnBuyName = Template.bind({})
16
+ WithOnBuyName.args = { xnsName: 'testing123', onBuyName: (name: string) => Promise.resolve(alert(`Buy Name: ${name}`)) }
17
+
18
+ export { Default, WithOnBuyName }
@@ -0,0 +1,66 @@
1
+ import { ArrowForwardRounded } from '@mui/icons-material'
2
+ import { Stack } from '@mui/material'
3
+ import type { LinkExProps } from '@xylabs/react-link'
4
+ import { LinkEx } from '@xylabs/react-link'
5
+ import { XnsNameHelper } from '@xyo-network/xns-record-payloadset-plugins'
6
+ import type { Dispatch } from 'react'
7
+ import React from 'react'
8
+
9
+ import type {
10
+ XnsNameCaptureBuyCallbacks, XnsNameCaptureRoutingProps, XnsNameCaptureTrackingProps,
11
+ } from './Props.ts'
12
+
13
+ export interface XnsCaptureSecondaryLinkProps extends XnsNameCaptureTrackingProps, XnsNameCaptureRoutingProps, XnsNameCaptureBuyCallbacks, LinkExProps {
14
+ event?: string
15
+ funnel?: string
16
+ placement?: string
17
+ setError?: Dispatch<Error | undefined>
18
+ text?: string
19
+ xnsName: string
20
+ }
21
+
22
+ export const XnsCaptureSecondaryLink: React.FC<XnsCaptureSecondaryLinkProps> = ({
23
+ event = 'Click to Reservation',
24
+ funnel = 'xns',
25
+ mixpanel,
26
+ navigate,
27
+ onBuyName,
28
+ paramsString = '',
29
+ placement = '',
30
+ setError,
31
+ text = 'Or make a free reservation',
32
+ to = '/xns/reservation',
33
+ userEvents,
34
+ xnsName,
35
+ ...props
36
+ }) => {
37
+ return (
38
+ <LinkEx
39
+ paddingX={0}
40
+ color="inherit"
41
+ style={{ textDecoration: 'underline', textUnderlineOffset: '5px' }}
42
+ onClick={async () => {
43
+ mixpanel?.track(event, {
44
+ Funnel: funnel,
45
+ Placement: placement,
46
+ })
47
+ const formattedXnsName = `${xnsName}.xyo`
48
+ const helper = XnsNameHelper.fromString(formattedXnsName)
49
+ const [valid, errors] = await helper.validate()
50
+ if (valid) {
51
+ await userEvents?.userClick({ elementName: event, elementType: 'xns-cta' })
52
+ navigate?.(`${to}?username=${xnsName}${paramsString}`)
53
+ await onBuyName?.(xnsName)
54
+ } else {
55
+ setError?.(new Error(errors.join(', ')))
56
+ }
57
+ }}
58
+ {...props}
59
+ >
60
+ <Stack flexDirection="row" gap={0.5} alignItems="center" sx={{ cursor: 'pointer' }}>
61
+ {text}
62
+ <ArrowForwardRounded />
63
+ </Stack>
64
+ </LinkEx>
65
+ )
66
+ }