@sproutsocial/seeds-react-select 1.0.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.
package/.eslintignore ADDED
@@ -0,0 +1,6 @@
1
+ # Node modules
2
+ node_modules/
3
+
4
+ # Build output
5
+ dist/
6
+ coverage/
package/.eslintrc.js ADDED
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ root: true,
3
+ extends: ["eslint-config-seeds/racine"],
4
+ };
@@ -0,0 +1,21 @@
1
+ yarn run v1.22.22
2
+ $ tsup --dts
3
+ CLI Building entry: src/index.ts
4
+ CLI Using tsconfig: tsconfig.json
5
+ CLI tsup v8.0.2
6
+ CLI Using tsup config: /home/runner/work/seeds/seeds/seeds-react/seeds-react-select/tsup.config.ts
7
+ CLI Target: es2022
8
+ CLI Cleaning output folder
9
+ CJS Build start
10
+ ESM Build start
11
+ CJS dist/index.js 6.66 KB
12
+ CJS dist/index.js.map 9.82 KB
13
+ CJS ⚡️ Build success in 163ms
14
+ ESM dist/esm/index.js 4.70 KB
15
+ ESM dist/esm/index.js.map 9.74 KB
16
+ ESM ⚡️ Build success in 168ms
17
+ DTS Build start
18
+ DTS ⚡️ Build success in 37165ms
19
+ DTS dist/index.d.ts 1.52 KB
20
+ DTS dist/index.d.mts 1.52 KB
21
+ Done in 43.16s.
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # @sproutsocial/seeds-react-select
2
+
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 05f7968: Migrate Select component to seeds-react-select
@@ -0,0 +1,188 @@
1
+ // src/Select.tsx
2
+ import "react";
3
+ import Icon from "@sproutsocial/seeds-react-icon";
4
+
5
+ // src/styles.ts
6
+ import styled, { css } from "styled-components";
7
+ import { focusRing } from "@sproutsocial/seeds-react-mixins";
8
+ import { COMMON } from "@sproutsocial/seeds-react-system-props";
9
+ var Container = styled.div`
10
+ position: relative;
11
+ box-sizing: border-box;
12
+
13
+ select {
14
+ box-sizing: border-box;
15
+ width: 100%;
16
+ border: 1px solid ${(props) => props.theme.colors.form.border.base};
17
+ border-radius: ${(props) => props.theme.radii[500]};
18
+ background-color: ${(props) => props.theme.colors.form.background.base};
19
+ color: ${(props) => props.theme.colors.text.body};
20
+ cursor: ${(props) => props.disabled ? "not-allowed" : "pointer"};
21
+ outline: none;
22
+ appearance: none;
23
+ transition: border-color ${(props) => props.theme.duration.fast}
24
+ ${(props) => props.theme.easing.ease_in},
25
+ box-shadow ${(props) => props.theme.duration.fast}
26
+ ${(props) => props.theme.easing.ease_in};
27
+ font-family: ${(props) => props.theme.fontFamily};
28
+ font-weight: ${(props) => props.theme.fontWeights.normal};
29
+ margin: 0;
30
+ /* We do this because the Sprout app sets it to hidden in Classic mode. We can delete after Nectar launches. */
31
+ visibility: visible;
32
+
33
+ padding: ${(props) => {
34
+ switch (props.size) {
35
+ case "large":
36
+ return `${props.theme.space[350]} ${props.theme.space[600]} ${props.theme.space[350]} ${props.theme.space[400]}`;
37
+ case "small":
38
+ return `${props.theme.space[200]} ${props.theme.space[500]} ${props.theme.space[200]} ${props.theme.space[200]}`;
39
+ case "default":
40
+ default:
41
+ return `${props.theme.space[300]} ${props.theme.space[500]} ${props.theme.space[300]} ${props.theme.space[300]}`;
42
+ }
43
+ }};
44
+ font-size: ${(props) => {
45
+ switch (props.size) {
46
+ case "large":
47
+ return props.theme.typography[300].fontSize;
48
+ case "small":
49
+ case "default":
50
+ default:
51
+ return props.theme.typography[200].fontSize;
52
+ }
53
+ }};
54
+
55
+ line-height: ${(props) => {
56
+ switch (props.size) {
57
+ case "large":
58
+ return props.theme.typography[300].lineHeight;
59
+ case "small":
60
+ case "default":
61
+ default:
62
+ return "normal";
63
+ }
64
+ }};
65
+
66
+ /* kill the dropdown arrow on IE 11 */
67
+ &::-ms-expand {
68
+ display: none;
69
+ }
70
+
71
+ &:focus {
72
+ ${focusRing}
73
+ }
74
+
75
+ /* Fix for red ring when input is marked required in Firefox */
76
+ &:not(output):not(:focus):-moz-ui-invalid {
77
+ box-shadow: none;
78
+ }
79
+ }
80
+
81
+ ${(props) => props.disabled && css`
82
+ opacity: 0.4;
83
+ `}
84
+
85
+ ${(props) => props.invalid && css`
86
+ select {
87
+ border-color: ${(props2) => props2.theme.colors.form.border.error};
88
+ }
89
+
90
+ ${Arrow} {
91
+ color: ${(props2) => props2.theme.colors.icon.error};
92
+ }
93
+ `}
94
+
95
+ ${COMMON}
96
+ `;
97
+ var Arrow = styled.span`
98
+ position: absolute;
99
+ top: 50%;
100
+ right: ${(props) => {
101
+ switch (props.size) {
102
+ case "large":
103
+ return props.theme.space[350];
104
+ case "small":
105
+ case "default":
106
+ default:
107
+ return props.theme.space[300];
108
+ }
109
+ }};
110
+ transform: translateY(-50%);
111
+ color: ${(props) => props.theme.colors.icon.base};
112
+ pointer-events: none;
113
+ `;
114
+ Container.displayName = "SelectContainer";
115
+ Arrow.displayName = "Select Arrow";
116
+ var styles_default = Container;
117
+
118
+ // src/Select.tsx
119
+ import { jsx, jsxs } from "react/jsx-runtime";
120
+ var Select = ({
121
+ id,
122
+ name,
123
+ children,
124
+ defaultValue,
125
+ value,
126
+ required,
127
+ isInvalid,
128
+ onChange,
129
+ autoFocus,
130
+ disabled,
131
+ ariaLabel,
132
+ ariaDescribedby,
133
+ size = "default",
134
+ qa = {},
135
+ inputProps = {},
136
+ ...rest
137
+ }) => {
138
+ const handleChange = (e) => {
139
+ if (onChange) {
140
+ onChange(e);
141
+ }
142
+ };
143
+ return /* @__PURE__ */ jsxs(styles_default, { invalid: isInvalid, disabled, size, ...rest, children: [
144
+ /* @__PURE__ */ jsx(
145
+ "select",
146
+ {
147
+ id,
148
+ name,
149
+ "aria-label": ariaLabel,
150
+ "aria-describedby": ariaDescribedby,
151
+ defaultValue,
152
+ value,
153
+ required,
154
+ autoFocus,
155
+ disabled,
156
+ onChange: handleChange,
157
+ "data-qa-select": name || "",
158
+ "data-qa-select-value": value || "unselected",
159
+ "data-qa-select-isrequired": required === true,
160
+ "data-qa-select-isdisabled": disabled === true,
161
+ ...qa,
162
+ ...inputProps,
163
+ children
164
+ }
165
+ ),
166
+ /* @__PURE__ */ jsx(Arrow, { size, children: /* @__PURE__ */ jsx(
167
+ Icon,
168
+ {
169
+ name: "chevron-down-outline",
170
+ fixedWidth: true,
171
+ size: size === "small" ? "small" : "medium",
172
+ "aria-hidden": true
173
+ }
174
+ ) })
175
+ ] });
176
+ };
177
+ var Select_default = Select;
178
+
179
+ // src/SelectTypes.ts
180
+ import "react";
181
+
182
+ // src/index.ts
183
+ var src_default = Select_default;
184
+ export {
185
+ Select_default as Select,
186
+ src_default as default
187
+ };
188
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/Select.tsx","../../src/styles.ts","../../src/SelectTypes.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\nimport Container, { Arrow } from \"./styles\";\nimport type { TypeSelectProps } from \"./SelectTypes\";\n\nconst Select = ({\n id,\n name,\n children,\n defaultValue,\n value,\n required,\n isInvalid,\n onChange,\n autoFocus,\n disabled,\n ariaLabel,\n ariaDescribedby,\n size = \"default\",\n qa = {},\n inputProps = {},\n ...rest\n}: TypeSelectProps) => {\n const handleChange = (e: React.SyntheticEvent<HTMLSelectElement>) => {\n if (onChange) {\n onChange(e);\n }\n };\n\n return (\n <Container invalid={isInvalid} disabled={disabled} size={size} {...rest}>\n <select\n id={id}\n name={name}\n aria-label={ariaLabel}\n aria-describedby={ariaDescribedby}\n defaultValue={defaultValue}\n value={value}\n required={required}\n autoFocus={autoFocus}\n disabled={disabled}\n onChange={handleChange}\n data-qa-select={name || \"\"}\n data-qa-select-value={value || \"unselected\"}\n data-qa-select-isrequired={required === true}\n data-qa-select-isdisabled={disabled === true}\n {...qa}\n {...inputProps}\n >\n {children}\n </select>\n\n <Arrow size={size}>\n <Icon\n name=\"chevron-down-outline\"\n fixedWidth\n size={size === \"small\" ? \"small\" : \"medium\"}\n aria-hidden\n />\n </Arrow>\n </Container>\n );\n};\n\nexport default Select;\n","import styled, { css } from \"styled-components\";\nimport { focusRing } from \"@sproutsocial/seeds-react-mixins\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeSelectProps } from \"./SelectTypes\";\n\ninterface TypeSelectContainer extends Omit<TypeSelectProps, \"isInvalid\"> {\n invalid?: boolean;\n}\n\nconst Container = styled.div<TypeSelectContainer>`\n position: relative;\n box-sizing: border-box;\n\n select {\n box-sizing: border-box;\n width: 100%;\n border: 1px solid ${(props) => props.theme.colors.form.border.base};\n border-radius: ${(props) => props.theme.radii[500]};\n background-color: ${(props) => props.theme.colors.form.background.base};\n color: ${(props) => props.theme.colors.text.body};\n cursor: ${(props) => (props.disabled ? \"not-allowed\" : \"pointer\")};\n outline: none;\n appearance: none;\n transition: border-color ${(props) => props.theme.duration.fast}\n ${(props) => props.theme.easing.ease_in},\n box-shadow ${(props) => props.theme.duration.fast}\n ${(props) => props.theme.easing.ease_in};\n font-family: ${(props) => props.theme.fontFamily};\n font-weight: ${(props) => props.theme.fontWeights.normal};\n margin: 0;\n /* We do this because the Sprout app sets it to hidden in Classic mode. We can delete after Nectar launches. */\n visibility: visible;\n\n padding: ${(props) => {\n switch (props.size) {\n case \"large\":\n return `${props.theme.space[350]} ${props.theme.space[600]} ${props.theme.space[350]} ${props.theme.space[400]}`;\n\n case \"small\":\n return `${props.theme.space[200]} ${props.theme.space[500]} ${props.theme.space[200]} ${props.theme.space[200]}`;\n\n case \"default\":\n default:\n return `${props.theme.space[300]} ${props.theme.space[500]} ${props.theme.space[300]} ${props.theme.space[300]}`;\n }\n }};\n font-size: ${(props) => {\n switch (props.size) {\n case \"large\":\n return props.theme.typography[300].fontSize;\n\n case \"small\":\n case \"default\":\n default:\n return props.theme.typography[200].fontSize;\n }\n }};\n\n line-height: ${(props) => {\n switch (props.size) {\n case \"large\":\n return props.theme.typography[300].lineHeight;\n\n /* hardcoded to 'normal' so the large change doesn't impact small/default */\n case \"small\":\n case \"default\":\n default:\n return \"normal\";\n }\n }};\n\n /* kill the dropdown arrow on IE 11 */\n &::-ms-expand {\n display: none;\n }\n\n &:focus {\n ${focusRing}\n }\n\n /* Fix for red ring when input is marked required in Firefox */\n &:not(output):not(:focus):-moz-ui-invalid {\n box-shadow: none;\n }\n }\n\n ${(props) =>\n props.disabled &&\n css`\n opacity: 0.4;\n `}\n\n ${(props) =>\n props.invalid &&\n css`\n select {\n border-color: ${(props) => props.theme.colors.form.border.error};\n }\n\n ${Arrow} {\n color: ${(props) => props.theme.colors.icon.error};\n }\n `}\n\n ${COMMON}\n`;\n\nexport const Arrow = styled.span<Pick<TypeSelectProps, \"size\">>`\n position: absolute;\n top: 50%;\n right: ${(props) => {\n switch (props.size) {\n case \"large\":\n return props.theme.space[350];\n case \"small\":\n case \"default\":\n default:\n return props.theme.space[300];\n }\n }};\n transform: translateY(-50%);\n color: ${(props) => props.theme.colors.icon.base};\n pointer-events: none;\n`;\n\nContainer.displayName = \"SelectContainer\";\nArrow.displayName = \"Select Arrow\";\n\nexport default Container;\n","import * as React from \"react\";\nimport type {\n TypeSystemCommonProps,\n TypeStyledComponentsCommonProps,\n} from \"@sproutsocial/seeds-react-system-props\";\n\nexport interface TypeSelectProps\n extends TypeStyledComponentsCommonProps,\n TypeSystemCommonProps,\n Omit<React.ComponentPropsWithoutRef<\"div\">, \"color\" | \"onChange\"> {\n /** ID of the form element, should match the \"for\" value of the associated label */\n id: string;\n name: string;\n\n /** Label used to describe the select if not used with an accompanying visual label */\n ariaLabel?: string;\n\n /** Attribute used to associate other elements that describe the Select, like an error */\n ariaDescribedby?: string;\n children: React.ReactNode;\n defaultValue?: React.ComponentPropsWithoutRef<\"select\">[\"value\"];\n\n /** Current value of the input */\n value?: React.ComponentPropsWithoutRef<\"select\">[\"value\"];\n required?: boolean;\n isInvalid?: boolean;\n autoFocus?: boolean;\n disabled?: boolean;\n\n /** Props to spread onto the underlying input element */\n inputProps?: React.ComponentPropsWithRef<\"select\">;\n qa?: object;\n onChange?: (e: React.SyntheticEvent<HTMLSelectElement>) => void;\n size?: \"large\" | \"small\" | \"default\";\n}\n","import Select from \"./Select\";\n\nexport default Select;\nexport { Select };\nexport * from \"./SelectTypes\";\n"],"mappings":"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","names":["props"]}
@@ -0,0 +1,30 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+ import { TypeStyledComponentsCommonProps, TypeSystemCommonProps } from '@sproutsocial/seeds-react-system-props';
4
+
5
+ interface TypeSelectProps extends TypeStyledComponentsCommonProps, TypeSystemCommonProps, Omit<React.ComponentPropsWithoutRef<"div">, "color" | "onChange"> {
6
+ /** ID of the form element, should match the "for" value of the associated label */
7
+ id: string;
8
+ name: string;
9
+ /** Label used to describe the select if not used with an accompanying visual label */
10
+ ariaLabel?: string;
11
+ /** Attribute used to associate other elements that describe the Select, like an error */
12
+ ariaDescribedby?: string;
13
+ children: React.ReactNode;
14
+ defaultValue?: React.ComponentPropsWithoutRef<"select">["value"];
15
+ /** Current value of the input */
16
+ value?: React.ComponentPropsWithoutRef<"select">["value"];
17
+ required?: boolean;
18
+ isInvalid?: boolean;
19
+ autoFocus?: boolean;
20
+ disabled?: boolean;
21
+ /** Props to spread onto the underlying input element */
22
+ inputProps?: React.ComponentPropsWithRef<"select">;
23
+ qa?: object;
24
+ onChange?: (e: React.SyntheticEvent<HTMLSelectElement>) => void;
25
+ size?: "large" | "small" | "default";
26
+ }
27
+
28
+ declare const Select: ({ id, name, children, defaultValue, value, required, isInvalid, onChange, autoFocus, disabled, ariaLabel, ariaDescribedby, size, qa, inputProps, ...rest }: TypeSelectProps) => react_jsx_runtime.JSX.Element;
29
+
30
+ export { Select, type TypeSelectProps, Select as default };
@@ -0,0 +1,30 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+ import { TypeStyledComponentsCommonProps, TypeSystemCommonProps } from '@sproutsocial/seeds-react-system-props';
4
+
5
+ interface TypeSelectProps extends TypeStyledComponentsCommonProps, TypeSystemCommonProps, Omit<React.ComponentPropsWithoutRef<"div">, "color" | "onChange"> {
6
+ /** ID of the form element, should match the "for" value of the associated label */
7
+ id: string;
8
+ name: string;
9
+ /** Label used to describe the select if not used with an accompanying visual label */
10
+ ariaLabel?: string;
11
+ /** Attribute used to associate other elements that describe the Select, like an error */
12
+ ariaDescribedby?: string;
13
+ children: React.ReactNode;
14
+ defaultValue?: React.ComponentPropsWithoutRef<"select">["value"];
15
+ /** Current value of the input */
16
+ value?: React.ComponentPropsWithoutRef<"select">["value"];
17
+ required?: boolean;
18
+ isInvalid?: boolean;
19
+ autoFocus?: boolean;
20
+ disabled?: boolean;
21
+ /** Props to spread onto the underlying input element */
22
+ inputProps?: React.ComponentPropsWithRef<"select">;
23
+ qa?: object;
24
+ onChange?: (e: React.SyntheticEvent<HTMLSelectElement>) => void;
25
+ size?: "large" | "small" | "default";
26
+ }
27
+
28
+ declare const Select: ({ id, name, children, defaultValue, value, required, isInvalid, onChange, autoFocus, disabled, ariaLabel, ariaDescribedby, size, qa, inputProps, ...rest }: TypeSelectProps) => react_jsx_runtime.JSX.Element;
29
+
30
+ export { Select, type TypeSelectProps, Select as default };
package/dist/index.js ADDED
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ Select: () => Select_default,
34
+ default: () => src_default
35
+ });
36
+ module.exports = __toCommonJS(src_exports);
37
+
38
+ // src/Select.tsx
39
+ var React = require("react");
40
+ var import_seeds_react_icon = __toESM(require("@sproutsocial/seeds-react-icon"));
41
+
42
+ // src/styles.ts
43
+ var import_styled_components = __toESM(require("styled-components"));
44
+ var import_seeds_react_mixins = require("@sproutsocial/seeds-react-mixins");
45
+ var import_seeds_react_system_props = require("@sproutsocial/seeds-react-system-props");
46
+ var Container = import_styled_components.default.div`
47
+ position: relative;
48
+ box-sizing: border-box;
49
+
50
+ select {
51
+ box-sizing: border-box;
52
+ width: 100%;
53
+ border: 1px solid ${(props) => props.theme.colors.form.border.base};
54
+ border-radius: ${(props) => props.theme.radii[500]};
55
+ background-color: ${(props) => props.theme.colors.form.background.base};
56
+ color: ${(props) => props.theme.colors.text.body};
57
+ cursor: ${(props) => props.disabled ? "not-allowed" : "pointer"};
58
+ outline: none;
59
+ appearance: none;
60
+ transition: border-color ${(props) => props.theme.duration.fast}
61
+ ${(props) => props.theme.easing.ease_in},
62
+ box-shadow ${(props) => props.theme.duration.fast}
63
+ ${(props) => props.theme.easing.ease_in};
64
+ font-family: ${(props) => props.theme.fontFamily};
65
+ font-weight: ${(props) => props.theme.fontWeights.normal};
66
+ margin: 0;
67
+ /* We do this because the Sprout app sets it to hidden in Classic mode. We can delete after Nectar launches. */
68
+ visibility: visible;
69
+
70
+ padding: ${(props) => {
71
+ switch (props.size) {
72
+ case "large":
73
+ return `${props.theme.space[350]} ${props.theme.space[600]} ${props.theme.space[350]} ${props.theme.space[400]}`;
74
+ case "small":
75
+ return `${props.theme.space[200]} ${props.theme.space[500]} ${props.theme.space[200]} ${props.theme.space[200]}`;
76
+ case "default":
77
+ default:
78
+ return `${props.theme.space[300]} ${props.theme.space[500]} ${props.theme.space[300]} ${props.theme.space[300]}`;
79
+ }
80
+ }};
81
+ font-size: ${(props) => {
82
+ switch (props.size) {
83
+ case "large":
84
+ return props.theme.typography[300].fontSize;
85
+ case "small":
86
+ case "default":
87
+ default:
88
+ return props.theme.typography[200].fontSize;
89
+ }
90
+ }};
91
+
92
+ line-height: ${(props) => {
93
+ switch (props.size) {
94
+ case "large":
95
+ return props.theme.typography[300].lineHeight;
96
+ case "small":
97
+ case "default":
98
+ default:
99
+ return "normal";
100
+ }
101
+ }};
102
+
103
+ /* kill the dropdown arrow on IE 11 */
104
+ &::-ms-expand {
105
+ display: none;
106
+ }
107
+
108
+ &:focus {
109
+ ${import_seeds_react_mixins.focusRing}
110
+ }
111
+
112
+ /* Fix for red ring when input is marked required in Firefox */
113
+ &:not(output):not(:focus):-moz-ui-invalid {
114
+ box-shadow: none;
115
+ }
116
+ }
117
+
118
+ ${(props) => props.disabled && import_styled_components.css`
119
+ opacity: 0.4;
120
+ `}
121
+
122
+ ${(props) => props.invalid && import_styled_components.css`
123
+ select {
124
+ border-color: ${(props2) => props2.theme.colors.form.border.error};
125
+ }
126
+
127
+ ${Arrow} {
128
+ color: ${(props2) => props2.theme.colors.icon.error};
129
+ }
130
+ `}
131
+
132
+ ${import_seeds_react_system_props.COMMON}
133
+ `;
134
+ var Arrow = import_styled_components.default.span`
135
+ position: absolute;
136
+ top: 50%;
137
+ right: ${(props) => {
138
+ switch (props.size) {
139
+ case "large":
140
+ return props.theme.space[350];
141
+ case "small":
142
+ case "default":
143
+ default:
144
+ return props.theme.space[300];
145
+ }
146
+ }};
147
+ transform: translateY(-50%);
148
+ color: ${(props) => props.theme.colors.icon.base};
149
+ pointer-events: none;
150
+ `;
151
+ Container.displayName = "SelectContainer";
152
+ Arrow.displayName = "Select Arrow";
153
+ var styles_default = Container;
154
+
155
+ // src/Select.tsx
156
+ var import_jsx_runtime = require("react/jsx-runtime");
157
+ var Select = ({
158
+ id,
159
+ name,
160
+ children,
161
+ defaultValue,
162
+ value,
163
+ required,
164
+ isInvalid,
165
+ onChange,
166
+ autoFocus,
167
+ disabled,
168
+ ariaLabel,
169
+ ariaDescribedby,
170
+ size = "default",
171
+ qa = {},
172
+ inputProps = {},
173
+ ...rest
174
+ }) => {
175
+ const handleChange = (e) => {
176
+ if (onChange) {
177
+ onChange(e);
178
+ }
179
+ };
180
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(styles_default, { invalid: isInvalid, disabled, size, ...rest, children: [
181
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
182
+ "select",
183
+ {
184
+ id,
185
+ name,
186
+ "aria-label": ariaLabel,
187
+ "aria-describedby": ariaDescribedby,
188
+ defaultValue,
189
+ value,
190
+ required,
191
+ autoFocus,
192
+ disabled,
193
+ onChange: handleChange,
194
+ "data-qa-select": name || "",
195
+ "data-qa-select-value": value || "unselected",
196
+ "data-qa-select-isrequired": required === true,
197
+ "data-qa-select-isdisabled": disabled === true,
198
+ ...qa,
199
+ ...inputProps,
200
+ children
201
+ }
202
+ ),
203
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Arrow, { size, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
204
+ import_seeds_react_icon.default,
205
+ {
206
+ name: "chevron-down-outline",
207
+ fixedWidth: true,
208
+ size: size === "small" ? "small" : "medium",
209
+ "aria-hidden": true
210
+ }
211
+ ) })
212
+ ] });
213
+ };
214
+ var Select_default = Select;
215
+
216
+ // src/SelectTypes.ts
217
+ var React2 = require("react");
218
+
219
+ // src/index.ts
220
+ var src_default = Select_default;
221
+ // Annotate the CommonJS export names for ESM import in node:
222
+ 0 && (module.exports = {
223
+ Select
224
+ });
225
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/Select.tsx","../src/styles.ts","../src/SelectTypes.ts"],"sourcesContent":["import Select from \"./Select\";\n\nexport default Select;\nexport { Select };\nexport * from \"./SelectTypes\";\n","import * as React from \"react\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\nimport Container, { Arrow } from \"./styles\";\nimport type { TypeSelectProps } from \"./SelectTypes\";\n\nconst Select = ({\n id,\n name,\n children,\n defaultValue,\n value,\n required,\n isInvalid,\n onChange,\n autoFocus,\n disabled,\n ariaLabel,\n ariaDescribedby,\n size = \"default\",\n qa = {},\n inputProps = {},\n ...rest\n}: TypeSelectProps) => {\n const handleChange = (e: React.SyntheticEvent<HTMLSelectElement>) => {\n if (onChange) {\n onChange(e);\n }\n };\n\n return (\n <Container invalid={isInvalid} disabled={disabled} size={size} {...rest}>\n <select\n id={id}\n name={name}\n aria-label={ariaLabel}\n aria-describedby={ariaDescribedby}\n defaultValue={defaultValue}\n value={value}\n required={required}\n autoFocus={autoFocus}\n disabled={disabled}\n onChange={handleChange}\n data-qa-select={name || \"\"}\n data-qa-select-value={value || \"unselected\"}\n data-qa-select-isrequired={required === true}\n data-qa-select-isdisabled={disabled === true}\n {...qa}\n {...inputProps}\n >\n {children}\n </select>\n\n <Arrow size={size}>\n <Icon\n name=\"chevron-down-outline\"\n fixedWidth\n size={size === \"small\" ? \"small\" : \"medium\"}\n aria-hidden\n />\n </Arrow>\n </Container>\n );\n};\n\nexport default Select;\n","import styled, { css } from \"styled-components\";\nimport { focusRing } from \"@sproutsocial/seeds-react-mixins\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeSelectProps } from \"./SelectTypes\";\n\ninterface TypeSelectContainer extends Omit<TypeSelectProps, \"isInvalid\"> {\n invalid?: boolean;\n}\n\nconst Container = styled.div<TypeSelectContainer>`\n position: relative;\n box-sizing: border-box;\n\n select {\n box-sizing: border-box;\n width: 100%;\n border: 1px solid ${(props) => props.theme.colors.form.border.base};\n border-radius: ${(props) => props.theme.radii[500]};\n background-color: ${(props) => props.theme.colors.form.background.base};\n color: ${(props) => props.theme.colors.text.body};\n cursor: ${(props) => (props.disabled ? \"not-allowed\" : \"pointer\")};\n outline: none;\n appearance: none;\n transition: border-color ${(props) => props.theme.duration.fast}\n ${(props) => props.theme.easing.ease_in},\n box-shadow ${(props) => props.theme.duration.fast}\n ${(props) => props.theme.easing.ease_in};\n font-family: ${(props) => props.theme.fontFamily};\n font-weight: ${(props) => props.theme.fontWeights.normal};\n margin: 0;\n /* We do this because the Sprout app sets it to hidden in Classic mode. We can delete after Nectar launches. */\n visibility: visible;\n\n padding: ${(props) => {\n switch (props.size) {\n case \"large\":\n return `${props.theme.space[350]} ${props.theme.space[600]} ${props.theme.space[350]} ${props.theme.space[400]}`;\n\n case \"small\":\n return `${props.theme.space[200]} ${props.theme.space[500]} ${props.theme.space[200]} ${props.theme.space[200]}`;\n\n case \"default\":\n default:\n return `${props.theme.space[300]} ${props.theme.space[500]} ${props.theme.space[300]} ${props.theme.space[300]}`;\n }\n }};\n font-size: ${(props) => {\n switch (props.size) {\n case \"large\":\n return props.theme.typography[300].fontSize;\n\n case \"small\":\n case \"default\":\n default:\n return props.theme.typography[200].fontSize;\n }\n }};\n\n line-height: ${(props) => {\n switch (props.size) {\n case \"large\":\n return props.theme.typography[300].lineHeight;\n\n /* hardcoded to 'normal' so the large change doesn't impact small/default */\n case \"small\":\n case \"default\":\n default:\n return \"normal\";\n }\n }};\n\n /* kill the dropdown arrow on IE 11 */\n &::-ms-expand {\n display: none;\n }\n\n &:focus {\n ${focusRing}\n }\n\n /* Fix for red ring when input is marked required in Firefox */\n &:not(output):not(:focus):-moz-ui-invalid {\n box-shadow: none;\n }\n }\n\n ${(props) =>\n props.disabled &&\n css`\n opacity: 0.4;\n `}\n\n ${(props) =>\n props.invalid &&\n css`\n select {\n border-color: ${(props) => props.theme.colors.form.border.error};\n }\n\n ${Arrow} {\n color: ${(props) => props.theme.colors.icon.error};\n }\n `}\n\n ${COMMON}\n`;\n\nexport const Arrow = styled.span<Pick<TypeSelectProps, \"size\">>`\n position: absolute;\n top: 50%;\n right: ${(props) => {\n switch (props.size) {\n case \"large\":\n return props.theme.space[350];\n case \"small\":\n case \"default\":\n default:\n return props.theme.space[300];\n }\n }};\n transform: translateY(-50%);\n color: ${(props) => props.theme.colors.icon.base};\n pointer-events: none;\n`;\n\nContainer.displayName = \"SelectContainer\";\nArrow.displayName = \"Select Arrow\";\n\nexport default Container;\n","import * as React from \"react\";\nimport type {\n TypeSystemCommonProps,\n TypeStyledComponentsCommonProps,\n} from \"@sproutsocial/seeds-react-system-props\";\n\nexport interface TypeSelectProps\n extends TypeStyledComponentsCommonProps,\n TypeSystemCommonProps,\n Omit<React.ComponentPropsWithoutRef<\"div\">, \"color\" | \"onChange\"> {\n /** ID of the form element, should match the \"for\" value of the associated label */\n id: string;\n name: string;\n\n /** Label used to describe the select if not used with an accompanying visual label */\n ariaLabel?: string;\n\n /** Attribute used to associate other elements that describe the Select, like an error */\n ariaDescribedby?: string;\n children: React.ReactNode;\n defaultValue?: React.ComponentPropsWithoutRef<\"select\">[\"value\"];\n\n /** Current value of the input */\n value?: React.ComponentPropsWithoutRef<\"select\">[\"value\"];\n required?: boolean;\n isInvalid?: boolean;\n autoFocus?: boolean;\n disabled?: boolean;\n\n /** Props to spread onto the underlying input element */\n inputProps?: React.ComponentPropsWithRef<\"select\">;\n qa?: object;\n onChange?: (e: React.SyntheticEvent<HTMLSelectElement>) => void;\n size?: \"large\" | \"small\" | \"default\";\n}\n"],"mappings":"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","names":["styled","props","Icon","React"]}
package/jest.config.js ADDED
@@ -0,0 +1,9 @@
1
+ const baseConfig = require("@sproutsocial/seeds-testing");
2
+
3
+ /** * @type {import('jest').Config} */
4
+ const config = {
5
+ ...baseConfig,
6
+ displayName: "seeds-react-select",
7
+ };
8
+
9
+ module.exports = config;
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@sproutsocial/seeds-react-select",
3
+ "version": "1.0.0",
4
+ "description": "Seeds React Select",
5
+ "author": "Sprout Social, Inc.",
6
+ "license": "MIT",
7
+ "main": "dist/index.js",
8
+ "module": "dist/esm/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "scripts": {
11
+ "build": "tsup --dts",
12
+ "build:debug": "tsup --dts --metafile",
13
+ "dev": "tsup --watch --dts",
14
+ "clean": "rm -rf .turbo dist",
15
+ "clean:modules": "rm -rf node_modules",
16
+ "typecheck": "tsc --noEmit",
17
+ "test": "jest",
18
+ "test:watch": "jest --watch --coverage=false"
19
+ },
20
+ "dependencies": {
21
+ "@sproutsocial/seeds-react-theme": "*",
22
+ "@sproutsocial/seeds-react-system-props": "*",
23
+ "@sproutsocial/seeds-react-icon": "*",
24
+ "@sproutsocial/seeds-react-mixins": "*"
25
+ },
26
+ "devDependencies": {
27
+ "@types/react": "^18.0.0",
28
+ "@types/styled-components": "^5.1.26",
29
+ "@sproutsocial/eslint-config-seeds": "*",
30
+ "react": "^18.0.0",
31
+ "styled-components": "^5.2.3",
32
+ "tsup": "^8.0.2",
33
+ "typescript": "^5.6.2",
34
+ "@sproutsocial/seeds-tsconfig": "*",
35
+ "@sproutsocial/seeds-testing": "*",
36
+ "@sproutsocial/seeds-react-testing-library": "*"
37
+ },
38
+ "peerDependencies": {
39
+ "styled-components": "^5.2.3"
40
+ },
41
+ "engines": {
42
+ "node": ">=18"
43
+ }
44
+ }
@@ -0,0 +1,53 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import Select from "./Select";
4
+
5
+ const meta: Meta<typeof Select> = {
6
+ title: "Components/Form Elements/Select",
7
+ component: Select,
8
+ argTypes: {
9
+ size: {
10
+ options: ["small", "large", undefined],
11
+ control: {
12
+ type: "select",
13
+ },
14
+ },
15
+ children: {
16
+ control: "object",
17
+ },
18
+ disabled: {
19
+ control: "boolean",
20
+ },
21
+ required: {
22
+ control: "boolean",
23
+ },
24
+ isInvalid: {
25
+ control: "boolean",
26
+ },
27
+ },
28
+ args: {
29
+ size: undefined,
30
+ disabled: false,
31
+ required: false,
32
+ isInvalid: false,
33
+ children: [
34
+ <option value="" disabled selected>
35
+ Select One
36
+ </option>,
37
+ <option value="apple">Apple</option>,
38
+ <option value="orange">Orange</option>,
39
+ <option value="banana">Banana</option>,
40
+ ],
41
+ },
42
+ };
43
+ export default meta;
44
+
45
+ type Story = StoryObj<typeof Select>;
46
+
47
+ export const Default: Story = {};
48
+
49
+ export const Large: Story = {
50
+ args: {
51
+ size: "large",
52
+ },
53
+ };
package/src/Select.tsx ADDED
@@ -0,0 +1,65 @@
1
+ import * as React from "react";
2
+ import Icon from "@sproutsocial/seeds-react-icon";
3
+ import Container, { Arrow } from "./styles";
4
+ import type { TypeSelectProps } from "./SelectTypes";
5
+
6
+ const Select = ({
7
+ id,
8
+ name,
9
+ children,
10
+ defaultValue,
11
+ value,
12
+ required,
13
+ isInvalid,
14
+ onChange,
15
+ autoFocus,
16
+ disabled,
17
+ ariaLabel,
18
+ ariaDescribedby,
19
+ size = "default",
20
+ qa = {},
21
+ inputProps = {},
22
+ ...rest
23
+ }: TypeSelectProps) => {
24
+ const handleChange = (e: React.SyntheticEvent<HTMLSelectElement>) => {
25
+ if (onChange) {
26
+ onChange(e);
27
+ }
28
+ };
29
+
30
+ return (
31
+ <Container invalid={isInvalid} disabled={disabled} size={size} {...rest}>
32
+ <select
33
+ id={id}
34
+ name={name}
35
+ aria-label={ariaLabel}
36
+ aria-describedby={ariaDescribedby}
37
+ defaultValue={defaultValue}
38
+ value={value}
39
+ required={required}
40
+ autoFocus={autoFocus}
41
+ disabled={disabled}
42
+ onChange={handleChange}
43
+ data-qa-select={name || ""}
44
+ data-qa-select-value={value || "unselected"}
45
+ data-qa-select-isrequired={required === true}
46
+ data-qa-select-isdisabled={disabled === true}
47
+ {...qa}
48
+ {...inputProps}
49
+ >
50
+ {children}
51
+ </select>
52
+
53
+ <Arrow size={size}>
54
+ <Icon
55
+ name="chevron-down-outline"
56
+ fixedWidth
57
+ size={size === "small" ? "small" : "medium"}
58
+ aria-hidden
59
+ />
60
+ </Arrow>
61
+ </Container>
62
+ );
63
+ };
64
+
65
+ export default Select;
@@ -0,0 +1,35 @@
1
+ import * as React from "react";
2
+ import type {
3
+ TypeSystemCommonProps,
4
+ TypeStyledComponentsCommonProps,
5
+ } from "@sproutsocial/seeds-react-system-props";
6
+
7
+ export interface TypeSelectProps
8
+ extends TypeStyledComponentsCommonProps,
9
+ TypeSystemCommonProps,
10
+ Omit<React.ComponentPropsWithoutRef<"div">, "color" | "onChange"> {
11
+ /** ID of the form element, should match the "for" value of the associated label */
12
+ id: string;
13
+ name: string;
14
+
15
+ /** Label used to describe the select if not used with an accompanying visual label */
16
+ ariaLabel?: string;
17
+
18
+ /** Attribute used to associate other elements that describe the Select, like an error */
19
+ ariaDescribedby?: string;
20
+ children: React.ReactNode;
21
+ defaultValue?: React.ComponentPropsWithoutRef<"select">["value"];
22
+
23
+ /** Current value of the input */
24
+ value?: React.ComponentPropsWithoutRef<"select">["value"];
25
+ required?: boolean;
26
+ isInvalid?: boolean;
27
+ autoFocus?: boolean;
28
+ disabled?: boolean;
29
+
30
+ /** Props to spread onto the underlying input element */
31
+ inputProps?: React.ComponentPropsWithRef<"select">;
32
+ qa?: object;
33
+ onChange?: (e: React.SyntheticEvent<HTMLSelectElement>) => void;
34
+ size?: "large" | "small" | "default";
35
+ }
@@ -0,0 +1,67 @@
1
+ import React from "react";
2
+ import {
3
+ render,
4
+ fireEvent,
5
+ screen,
6
+ } from "@sproutsocial/seeds-react-testing-library";
7
+ import Select from "../Select";
8
+
9
+ describe("Select", () => {
10
+ it("should notify on changes", () => {
11
+ const mockOnChange = jest.fn();
12
+ render(
13
+ <Select id="select" name="select" onChange={mockOnChange}>
14
+ <option value="v">option1</option>
15
+ </Select>
16
+ );
17
+ fireEvent.change(
18
+ screen.getByDataQaLabel({
19
+ select: "select",
20
+ })
21
+ );
22
+ expect(mockOnChange).toHaveBeenCalled();
23
+ });
24
+ it("should render statuses correctly", () => {
25
+ const { rerender } = render(
26
+ <Select id="select" name="select" disabled>
27
+ <option value="v">option1</option>
28
+ </Select>
29
+ );
30
+ expect(
31
+ screen.getByDataQaLabel({
32
+ "select-isdisabled": "true",
33
+ })
34
+ ).toBeTruthy();
35
+ rerender(
36
+ <Select id="select" name="select" disabled={false} isInvalid>
37
+ <option value="v">option1</option>
38
+ </Select>
39
+ );
40
+ expect(
41
+ screen.getByDataQaLabel({
42
+ "select-isdisabled": "false",
43
+ })
44
+ ).toBeTruthy();
45
+ });
46
+ it("should render with chevron-down", () => {
47
+ render(
48
+ <Select
49
+ id="select"
50
+ name="select"
51
+ inputProps={{
52
+ style: {
53
+ display: "block",
54
+ margin: "200",
55
+ },
56
+ }}
57
+ >
58
+ <option value="v">option1</option>
59
+ </Select>
60
+ );
61
+ expect(
62
+ screen.getByDataQaLabel({
63
+ icon: "chevron-down-outline",
64
+ })
65
+ ).toBeTruthy();
66
+ });
67
+ });
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ import Select from "./Select";
2
+
3
+ export default Select;
4
+ export { Select };
5
+ export * from "./SelectTypes";
@@ -0,0 +1,7 @@
1
+ import "styled-components";
2
+ import { TypeTheme } from "@sproutsocial/seeds-react-theme";
3
+
4
+ declare module "styled-components" {
5
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
6
+ export interface DefaultTheme extends TypeTheme {}
7
+ }
package/src/styles.ts ADDED
@@ -0,0 +1,129 @@
1
+ import styled, { css } from "styled-components";
2
+ import { focusRing } from "@sproutsocial/seeds-react-mixins";
3
+ import { COMMON } from "@sproutsocial/seeds-react-system-props";
4
+ import type { TypeSelectProps } from "./SelectTypes";
5
+
6
+ interface TypeSelectContainer extends Omit<TypeSelectProps, "isInvalid"> {
7
+ invalid?: boolean;
8
+ }
9
+
10
+ const Container = styled.div<TypeSelectContainer>`
11
+ position: relative;
12
+ box-sizing: border-box;
13
+
14
+ select {
15
+ box-sizing: border-box;
16
+ width: 100%;
17
+ border: 1px solid ${(props) => props.theme.colors.form.border.base};
18
+ border-radius: ${(props) => props.theme.radii[500]};
19
+ background-color: ${(props) => props.theme.colors.form.background.base};
20
+ color: ${(props) => props.theme.colors.text.body};
21
+ cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")};
22
+ outline: none;
23
+ appearance: none;
24
+ transition: border-color ${(props) => props.theme.duration.fast}
25
+ ${(props) => props.theme.easing.ease_in},
26
+ box-shadow ${(props) => props.theme.duration.fast}
27
+ ${(props) => props.theme.easing.ease_in};
28
+ font-family: ${(props) => props.theme.fontFamily};
29
+ font-weight: ${(props) => props.theme.fontWeights.normal};
30
+ margin: 0;
31
+ /* We do this because the Sprout app sets it to hidden in Classic mode. We can delete after Nectar launches. */
32
+ visibility: visible;
33
+
34
+ padding: ${(props) => {
35
+ switch (props.size) {
36
+ case "large":
37
+ return `${props.theme.space[350]} ${props.theme.space[600]} ${props.theme.space[350]} ${props.theme.space[400]}`;
38
+
39
+ case "small":
40
+ return `${props.theme.space[200]} ${props.theme.space[500]} ${props.theme.space[200]} ${props.theme.space[200]}`;
41
+
42
+ case "default":
43
+ default:
44
+ return `${props.theme.space[300]} ${props.theme.space[500]} ${props.theme.space[300]} ${props.theme.space[300]}`;
45
+ }
46
+ }};
47
+ font-size: ${(props) => {
48
+ switch (props.size) {
49
+ case "large":
50
+ return props.theme.typography[300].fontSize;
51
+
52
+ case "small":
53
+ case "default":
54
+ default:
55
+ return props.theme.typography[200].fontSize;
56
+ }
57
+ }};
58
+
59
+ line-height: ${(props) => {
60
+ switch (props.size) {
61
+ case "large":
62
+ return props.theme.typography[300].lineHeight;
63
+
64
+ /* hardcoded to 'normal' so the large change doesn't impact small/default */
65
+ case "small":
66
+ case "default":
67
+ default:
68
+ return "normal";
69
+ }
70
+ }};
71
+
72
+ /* kill the dropdown arrow on IE 11 */
73
+ &::-ms-expand {
74
+ display: none;
75
+ }
76
+
77
+ &:focus {
78
+ ${focusRing}
79
+ }
80
+
81
+ /* Fix for red ring when input is marked required in Firefox */
82
+ &:not(output):not(:focus):-moz-ui-invalid {
83
+ box-shadow: none;
84
+ }
85
+ }
86
+
87
+ ${(props) =>
88
+ props.disabled &&
89
+ css`
90
+ opacity: 0.4;
91
+ `}
92
+
93
+ ${(props) =>
94
+ props.invalid &&
95
+ css`
96
+ select {
97
+ border-color: ${(props) => props.theme.colors.form.border.error};
98
+ }
99
+
100
+ ${Arrow} {
101
+ color: ${(props) => props.theme.colors.icon.error};
102
+ }
103
+ `}
104
+
105
+ ${COMMON}
106
+ `;
107
+
108
+ export const Arrow = styled.span<Pick<TypeSelectProps, "size">>`
109
+ position: absolute;
110
+ top: 50%;
111
+ right: ${(props) => {
112
+ switch (props.size) {
113
+ case "large":
114
+ return props.theme.space[350];
115
+ case "small":
116
+ case "default":
117
+ default:
118
+ return props.theme.space[300];
119
+ }
120
+ }};
121
+ transform: translateY(-50%);
122
+ color: ${(props) => props.theme.colors.icon.base};
123
+ pointer-events: none;
124
+ `;
125
+
126
+ Container.displayName = "SelectContainer";
127
+ Arrow.displayName = "Select Arrow";
128
+
129
+ export default Container;
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "@sproutsocial/seeds-tsconfig/bundler/dom/library-monorepo",
3
+ "compilerOptions": {
4
+ "jsx": "react-jsx",
5
+ "module": "esnext"
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["node_modules", "dist", "coverage"]
9
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig((options) => ({
4
+ entry: ["src/index.ts"],
5
+ format: ["cjs", "esm"],
6
+ clean: true,
7
+ legacyOutput: true,
8
+ dts: options.dts,
9
+ external: ["react"],
10
+ sourcemap: true,
11
+ metafile: options.metafile,
12
+ }));