@webstudio-is/sdk-components-react-remix 0.75.0 → 0.76.0

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.
@@ -24,6 +24,7 @@ __export(form_ws_exports, {
24
24
  module.exports = __toCommonJS(form_ws_exports);
25
25
  var import_svg = require("@webstudio-is/icons/svg");
26
26
  var import_css_normalize = require("@webstudio-is/react-sdk/css-normalize");
27
+ var import_react_sdk = require("@webstudio-is/react-sdk");
27
28
  var import_form = require("./__generated__/form.props");
28
29
  const presetStyle = {
29
30
  form: [
@@ -34,6 +35,7 @@ const presetStyle = {
34
35
  const meta = {
35
36
  category: "forms",
36
37
  type: "container",
38
+ invalidAncestors: ["Form"],
37
39
  label: "Form",
38
40
  icon: import_svg.FormIcon,
39
41
  presetStyle,
@@ -46,59 +48,101 @@ const meta = {
46
48
  {
47
49
  type: "instance",
48
50
  component: "Form",
49
- children: [
50
- {
51
- type: "instance",
52
- component: "Label",
53
- children: [{ type: "text", value: "Name" }]
54
- },
55
- {
56
- type: "instance",
57
- component: "Input",
58
- props: [{ type: "string", name: "name", value: "name" }],
59
- children: []
60
- },
61
- {
62
- type: "instance",
63
- component: "Label",
64
- children: [{ type: "text", value: "Email" }]
65
- },
51
+ props: [
66
52
  {
67
- type: "instance",
68
- component: "Input",
69
- props: [{ type: "string", name: "name", value: "email" }],
70
- children: []
71
- },
72
- {
73
- type: "instance",
74
- component: "Button",
75
- children: [{ type: "text", value: "Submit" }]
76
- },
53
+ name: "state",
54
+ type: "string",
55
+ value: "initial",
56
+ dataSourceRef: {
57
+ type: "variable",
58
+ name: "formState"
59
+ }
60
+ }
61
+ ],
62
+ children: [
77
63
  {
78
64
  type: "instance",
79
- component: "SuccessMessage",
65
+ label: "Form Content",
66
+ component: "Box",
67
+ props: [
68
+ {
69
+ name: import_react_sdk.showAttribute,
70
+ type: "boolean",
71
+ value: false,
72
+ dataSourceRef: {
73
+ type: "expression",
74
+ name: "formInitial",
75
+ code: `formState === 'initial' || formState === 'error'`
76
+ }
77
+ }
78
+ ],
80
79
  children: [
81
80
  {
82
81
  type: "instance",
83
- component: "Text",
84
- children: [
85
- { type: "text", value: "Thank you for getting in touch!" }
86
- ]
82
+ component: "Label",
83
+ children: [{ type: "text", value: "Name" }]
84
+ },
85
+ {
86
+ type: "instance",
87
+ component: "Input",
88
+ props: [{ type: "string", name: "name", value: "name" }],
89
+ children: []
90
+ },
91
+ {
92
+ type: "instance",
93
+ component: "Label",
94
+ children: [{ type: "text", value: "Email" }]
95
+ },
96
+ {
97
+ type: "instance",
98
+ component: "Input",
99
+ props: [{ type: "string", name: "name", value: "email" }],
100
+ children: []
101
+ },
102
+ {
103
+ type: "instance",
104
+ component: "Button",
105
+ children: [{ type: "text", value: "Submit" }]
87
106
  }
88
107
  ]
89
108
  },
90
109
  {
91
110
  type: "instance",
92
- component: "ErrorMessage",
93
- children: [
111
+ label: "Success Message",
112
+ component: "Box",
113
+ props: [
94
114
  {
95
- type: "instance",
96
- component: "Text",
97
- children: [
98
- { type: "text", value: "Sorry, something went wrong." }
99
- ]
115
+ name: import_react_sdk.showAttribute,
116
+ type: "boolean",
117
+ value: false,
118
+ dataSourceRef: {
119
+ type: "expression",
120
+ name: "formSuccess",
121
+ code: `formState === 'success'`
122
+ }
100
123
  }
124
+ ],
125
+ children: [
126
+ { type: "text", value: "Thank you for getting in touch!" }
101
127
  ]
128
+ },
129
+ {
130
+ type: "instance",
131
+ label: "Error Message",
132
+ component: "Box",
133
+ props: [
134
+ {
135
+ name: import_react_sdk.showAttribute,
136
+ type: "boolean",
137
+ value: false,
138
+ dataSourceRef: {
139
+ type: "expression",
140
+ name: "formError",
141
+ code: `formState === 'error'`
142
+ }
143
+ }
144
+ ],
145
+ children: [{ type: "text", value: "Sorry, something went wrong." }]
102
146
  }
103
147
  ]
104
148
  }
@@ -106,5 +150,5 @@ const meta = {
106
150
  };
107
151
  const propsMeta = {
108
152
  props: import_form.props,
109
- initialProps: ["initialState"]
153
+ initialProps: ["state"]
110
154
  };
@@ -30,8 +30,11 @@ const wrapLinkComponent = (BaseLink) => {
30
30
  const href = (0, import_react_sdk.usePropUrl)((0, import_react_sdk.getInstanceIdFromComponentProps)(props), "href");
31
31
  if (href?.type === "page") {
32
32
  let to = href.page.path === "" ? "/" : href.page.path;
33
+ const urlTo = new URL(to, "https://any-valid.url");
34
+ to = urlTo.pathname;
33
35
  if (href.hash !== void 0) {
34
- to += `#${href.hash}`;
36
+ urlTo.hash = encodeURIComponent(href.hash);
37
+ to = `${urlTo.pathname}${urlTo.hash}`;
35
38
  }
36
39
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.NavLink, { ...props, to, ref });
37
40
  }
package/lib/form.js CHANGED
@@ -1,61 +1,39 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import {
3
- Children,
4
- cloneElement,
5
- forwardRef
3
+ forwardRef,
4
+ useRef,
5
+ useEffect,
6
+ useContext
6
7
  } from "react";
7
8
  import { useFetcher } from "@remix-run/react";
8
9
  import { formIdFieldName } from "@webstudio-is/form-handlers";
9
- import { getInstanceIdFromComponentProps } from "@webstudio-is/react-sdk";
10
+ import {
11
+ ReactSdkContext,
12
+ getInstanceIdFromComponentProps
13
+ } from "@webstudio-is/react-sdk";
10
14
  const defaultTag = "form";
11
- const isComponentNode = (component, node) => "props" in node && node.props.instance?.component === component;
12
- const onlyErrorMessage = (children) => Children.map(children, (child) => {
13
- if (typeof child !== "object" || child === null) {
14
- return null;
15
- }
16
- if (isComponentNode("ErrorMessage", child)) {
17
- return child;
18
- }
19
- if ("props" in child) {
20
- const newChildren = onlyErrorMessage(child.props.children);
21
- return Children.toArray(newChildren).some((child2) => child2 !== null) ? cloneElement(child, { children: newChildren }) : null;
22
- }
23
- return onlyErrorMessage(child);
24
- });
25
- const onlySuccessMessage = (children) => Children.map(children, (child) => {
26
- if (typeof child !== "object" || child === null) {
27
- return null;
28
- }
29
- if (isComponentNode("SuccessMessage", child)) {
30
- return child;
31
- }
32
- if ("props" in child) {
33
- const newChildren = onlySuccessMessage(child.props.children);
34
- return Children.toArray(newChildren).some((child2) => child2 !== null) ? cloneElement(child, { children: newChildren }) : null;
35
- }
36
- return onlySuccessMessage(child);
37
- });
38
- const withoutMessages = (children) => Children.map(children, (child) => {
39
- if (typeof child !== "object" || child === null) {
40
- return child;
41
- }
42
- if (isComponentNode("ErrorMessage", child) || isComponentNode("SuccessMessage", child)) {
43
- return null;
44
- }
45
- if ("props" in child) {
46
- return cloneElement(child, {
47
- children: withoutMessages(child.props.children)
48
- });
49
- }
50
- return withoutMessages(child);
51
- });
52
- const Form = forwardRef(({ children, initialState = "initial", ...props }, ref) => {
15
+ const useOnFetchEnd = (fetcher, handler) => {
16
+ const latestHandler = useRef(handler);
17
+ latestHandler.current = handler;
18
+ const prevFetcher = useRef(fetcher);
19
+ useEffect(() => {
20
+ if (prevFetcher.current.state !== fetcher.state && fetcher.state === "idle" && fetcher.data !== void 0) {
21
+ latestHandler.current(fetcher.data);
22
+ }
23
+ prevFetcher.current = fetcher;
24
+ }, [fetcher]);
25
+ };
26
+ const Form = forwardRef(({ children, action, method, state = "initial", ...rest }, ref) => {
27
+ const { setDataSourceValue } = useContext(ReactSdkContext);
53
28
  const fetcher = useFetcher();
54
- const state = fetcher.type === "done" ? fetcher.data?.success === true ? "success" : "error" : initialState;
55
- const instanceId = getInstanceIdFromComponentProps(props);
56
- return /* @__PURE__ */ jsxs(fetcher.Form, { ...props, method: "post", "data-state": state, ref, children: [
29
+ const instanceId = getInstanceIdFromComponentProps(rest);
30
+ useOnFetchEnd(fetcher, (data) => {
31
+ const state2 = data?.success === true ? "success" : "error";
32
+ setDataSourceValue(instanceId, "state", state2);
33
+ });
34
+ return /* @__PURE__ */ jsxs(fetcher.Form, { ...rest, method: "post", "data-state": state, ref, children: [
57
35
  /* @__PURE__ */ jsx("input", { type: "hidden", name: formIdFieldName, value: instanceId }),
58
- state === "success" ? onlySuccessMessage(children) : state === "error" ? onlyErrorMessage(children) : withoutMessages(children)
36
+ children
59
37
  ] });
60
38
  });
61
39
  Form.displayName = "Form";
package/lib/form.ws.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import { FormIcon } from "@webstudio-is/icons/svg";
2
2
  import { form } from "@webstudio-is/react-sdk/css-normalize";
3
+ import {
4
+ showAttribute
5
+ } from "@webstudio-is/react-sdk";
3
6
  import { props } from "./__generated__/form.props";
4
7
  const presetStyle = {
5
8
  form: [
@@ -10,6 +13,7 @@ const presetStyle = {
10
13
  const meta = {
11
14
  category: "forms",
12
15
  type: "container",
16
+ invalidAncestors: ["Form"],
13
17
  label: "Form",
14
18
  icon: FormIcon,
15
19
  presetStyle,
@@ -22,59 +26,101 @@ const meta = {
22
26
  {
23
27
  type: "instance",
24
28
  component: "Form",
25
- children: [
26
- {
27
- type: "instance",
28
- component: "Label",
29
- children: [{ type: "text", value: "Name" }]
30
- },
31
- {
32
- type: "instance",
33
- component: "Input",
34
- props: [{ type: "string", name: "name", value: "name" }],
35
- children: []
36
- },
37
- {
38
- type: "instance",
39
- component: "Label",
40
- children: [{ type: "text", value: "Email" }]
41
- },
29
+ props: [
42
30
  {
43
- type: "instance",
44
- component: "Input",
45
- props: [{ type: "string", name: "name", value: "email" }],
46
- children: []
47
- },
48
- {
49
- type: "instance",
50
- component: "Button",
51
- children: [{ type: "text", value: "Submit" }]
52
- },
31
+ name: "state",
32
+ type: "string",
33
+ value: "initial",
34
+ dataSourceRef: {
35
+ type: "variable",
36
+ name: "formState"
37
+ }
38
+ }
39
+ ],
40
+ children: [
53
41
  {
54
42
  type: "instance",
55
- component: "SuccessMessage",
43
+ label: "Form Content",
44
+ component: "Box",
45
+ props: [
46
+ {
47
+ name: showAttribute,
48
+ type: "boolean",
49
+ value: false,
50
+ dataSourceRef: {
51
+ type: "expression",
52
+ name: "formInitial",
53
+ code: `formState === 'initial' || formState === 'error'`
54
+ }
55
+ }
56
+ ],
56
57
  children: [
57
58
  {
58
59
  type: "instance",
59
- component: "Text",
60
- children: [
61
- { type: "text", value: "Thank you for getting in touch!" }
62
- ]
60
+ component: "Label",
61
+ children: [{ type: "text", value: "Name" }]
62
+ },
63
+ {
64
+ type: "instance",
65
+ component: "Input",
66
+ props: [{ type: "string", name: "name", value: "name" }],
67
+ children: []
68
+ },
69
+ {
70
+ type: "instance",
71
+ component: "Label",
72
+ children: [{ type: "text", value: "Email" }]
73
+ },
74
+ {
75
+ type: "instance",
76
+ component: "Input",
77
+ props: [{ type: "string", name: "name", value: "email" }],
78
+ children: []
79
+ },
80
+ {
81
+ type: "instance",
82
+ component: "Button",
83
+ children: [{ type: "text", value: "Submit" }]
63
84
  }
64
85
  ]
65
86
  },
66
87
  {
67
88
  type: "instance",
68
- component: "ErrorMessage",
69
- children: [
89
+ label: "Success Message",
90
+ component: "Box",
91
+ props: [
70
92
  {
71
- type: "instance",
72
- component: "Text",
73
- children: [
74
- { type: "text", value: "Sorry, something went wrong." }
75
- ]
93
+ name: showAttribute,
94
+ type: "boolean",
95
+ value: false,
96
+ dataSourceRef: {
97
+ type: "expression",
98
+ name: "formSuccess",
99
+ code: `formState === 'success'`
100
+ }
76
101
  }
102
+ ],
103
+ children: [
104
+ { type: "text", value: "Thank you for getting in touch!" }
77
105
  ]
106
+ },
107
+ {
108
+ type: "instance",
109
+ label: "Error Message",
110
+ component: "Box",
111
+ props: [
112
+ {
113
+ name: showAttribute,
114
+ type: "boolean",
115
+ value: false,
116
+ dataSourceRef: {
117
+ type: "expression",
118
+ name: "formError",
119
+ code: `formState === 'error'`
120
+ }
121
+ }
122
+ ],
123
+ children: [{ type: "text", value: "Sorry, something went wrong." }]
78
124
  }
79
125
  ]
80
126
  }
@@ -82,7 +128,7 @@ const meta = {
82
128
  };
83
129
  const propsMeta = {
84
130
  props,
85
- initialProps: ["initialState"]
131
+ initialProps: ["state"]
86
132
  };
87
133
  export {
88
134
  meta,
@@ -10,8 +10,11 @@ const wrapLinkComponent = (BaseLink) => {
10
10
  const href = usePropUrl(getInstanceIdFromComponentProps(props), "href");
11
11
  if (href?.type === "page") {
12
12
  let to = href.page.path === "" ? "/" : href.page.path;
13
+ const urlTo = new URL(to, "https://any-valid.url");
14
+ to = urlTo.pathname;
13
15
  if (href.hash !== void 0) {
14
- to += `#${href.hash}`;
16
+ urlTo.hash = encodeURIComponent(href.hash);
17
+ to = `${urlTo.pathname}${urlTo.hash}`;
15
18
  }
16
19
  return /* @__PURE__ */ jsx(RemixLink, { ...props, to, ref });
17
20
  }
@@ -1,5 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  export declare const defaultTag = "form";
3
- export declare const Form: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>, "action" | "method"> & {
4
- initialState?: "initial" | "error" | "success" | undefined;
3
+ type State = "initial" | "success" | "error";
4
+ export declare const Form: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLFormElement> & import("react").FormHTMLAttributes<HTMLFormElement> & {
5
+ state?: State | undefined;
5
6
  }, "ref"> & import("react").RefAttributes<HTMLFormElement>>;
7
+ export {};
@@ -1,3 +1,3 @@
1
- import type { WsComponentMeta, WsComponentPropsMeta } from "@webstudio-is/react-sdk";
1
+ import { type WsComponentMeta, type WsComponentPropsMeta } from "@webstudio-is/react-sdk";
2
2
  export declare const meta: WsComponentMeta;
3
3
  export declare const propsMeta: WsComponentPropsMeta;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webstudio-is/sdk-components-react-remix",
3
- "version": "0.75.0",
3
+ "version": "0.76.0",
4
4
  "description": "Webstudio components for Remix",
5
5
  "author": "Webstudio <github@webstudio.is>",
6
6
  "homepage": "https://webstudio.is",
@@ -38,11 +38,11 @@
38
38
  "react-dom": "^18.2.0"
39
39
  },
40
40
  "dependencies": {
41
- "@webstudio-is/form-handlers": "^0.75.0",
42
- "@webstudio-is/generate-arg-types": "^0.75.0",
43
- "@webstudio-is/icons": "^0.75.0",
44
- "@webstudio-is/react-sdk": "^0.75.0",
45
- "@webstudio-is/sdk-components-react": "^0.75.0"
41
+ "@webstudio-is/form-handlers": "^0.76.0",
42
+ "@webstudio-is/generate-arg-types": "^0.76.0",
43
+ "@webstudio-is/icons": "^0.76.0",
44
+ "@webstudio-is/react-sdk": "^0.76.0",
45
+ "@webstudio-is/sdk-components-react": "^0.76.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@remix-run/react": "^1.15.0",
@@ -60,7 +60,6 @@
60
60
  "build:args": "generate-arg-types './src/*.tsx !./src/**/*.stories.tsx !./src/**/*.ws.tsx' && prettier --write \"**/*.props.ts\"",
61
61
  "dts": "tsc --project tsconfig.dts.json",
62
62
  "typecheck": "tsc --noEmit --emitDeclarationOnly false",
63
- "lint": "eslint ./src --ext .ts,.tsx --max-warnings 0",
64
- "checks": "pnpm typecheck && pnpm lint"
63
+ "checks": "pnpm typecheck"
65
64
  }
66
65
  }