@tonyarbor/components 0.1.0 → 0.2.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/dist/Checkbox.d.mts +48 -0
- package/dist/Checkbox.d.ts +48 -0
- package/dist/Checkbox.js +165 -0
- package/dist/Checkbox.js.map +1 -0
- package/dist/Checkbox.mjs +7 -0
- package/dist/Checkbox.mjs.map +1 -0
- package/dist/Combobox.d.mts +67 -0
- package/dist/Combobox.d.ts +67 -0
- package/dist/Combobox.js +329 -0
- package/dist/Combobox.js.map +1 -0
- package/dist/Combobox.mjs +7 -0
- package/dist/Combobox.mjs.map +1 -0
- package/dist/NumericInput.d.mts +68 -0
- package/dist/NumericInput.d.ts +68 -0
- package/dist/NumericInput.js +319 -0
- package/dist/NumericInput.js.map +1 -0
- package/dist/NumericInput.mjs +7 -0
- package/dist/NumericInput.mjs.map +1 -0
- package/dist/Radio.d.mts +48 -0
- package/dist/Radio.d.ts +48 -0
- package/dist/Radio.js +174 -0
- package/dist/Radio.js.map +1 -0
- package/dist/Radio.mjs +7 -0
- package/dist/Radio.mjs.map +1 -0
- package/dist/chunk-5BUXFTPW.mjs +283 -0
- package/dist/chunk-5BUXFTPW.mjs.map +1 -0
- package/dist/chunk-7OWLBYNM.mjs +293 -0
- package/dist/chunk-7OWLBYNM.mjs.map +1 -0
- package/dist/chunk-ARBHNHO7.mjs +138 -0
- package/dist/chunk-ARBHNHO7.mjs.map +1 -0
- package/dist/chunk-BCYJIUQX.mjs +129 -0
- package/dist/chunk-BCYJIUQX.mjs.map +1 -0
- package/dist/index.d.mts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +835 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +16 -0
- package/package.json +24 -3
|
@@ -0,0 +1,319 @@
|
|
|
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/NumericInput/index.ts
|
|
31
|
+
var NumericInput_exports = {};
|
|
32
|
+
__export(NumericInput_exports, {
|
|
33
|
+
NumericInput: () => NumericInput
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(NumericInput_exports);
|
|
36
|
+
|
|
37
|
+
// src/NumericInput/NumericInput.tsx
|
|
38
|
+
var React = __toESM(require("react"));
|
|
39
|
+
var import_clsx = require("clsx");
|
|
40
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
41
|
+
var labelStyles = {
|
|
42
|
+
display: "block",
|
|
43
|
+
fontSize: "13px",
|
|
44
|
+
fontWeight: "600",
|
|
45
|
+
color: "#2f2f2f",
|
|
46
|
+
marginBottom: "4px",
|
|
47
|
+
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
|
|
48
|
+
};
|
|
49
|
+
var helperTextStyles = {
|
|
50
|
+
fontSize: "13px",
|
|
51
|
+
margin: "0",
|
|
52
|
+
marginTop: "2px",
|
|
53
|
+
color: "#595959",
|
|
54
|
+
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
|
55
|
+
lineHeight: "1.4"
|
|
56
|
+
};
|
|
57
|
+
var errorTextStyles = {
|
|
58
|
+
...helperTextStyles,
|
|
59
|
+
color: "#a62323",
|
|
60
|
+
// destructive-600
|
|
61
|
+
display: "flex",
|
|
62
|
+
alignItems: "center",
|
|
63
|
+
gap: "4px"
|
|
64
|
+
};
|
|
65
|
+
var containerStyles = {
|
|
66
|
+
base: {
|
|
67
|
+
display: "flex",
|
|
68
|
+
alignItems: "center",
|
|
69
|
+
height: "46px",
|
|
70
|
+
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
|
71
|
+
backgroundColor: "#ffffff",
|
|
72
|
+
borderRadius: "8px",
|
|
73
|
+
transition: "all 0.2s ease-in-out",
|
|
74
|
+
overflow: "hidden"
|
|
75
|
+
},
|
|
76
|
+
states: {
|
|
77
|
+
default: {
|
|
78
|
+
border: "1px solid #d1d1d1"
|
|
79
|
+
// grey-300
|
|
80
|
+
},
|
|
81
|
+
defaultFocus: {
|
|
82
|
+
borderColor: "#3cad51",
|
|
83
|
+
// brand-500
|
|
84
|
+
outline: "3px solid rgba(60, 173, 81, 0.2)"
|
|
85
|
+
},
|
|
86
|
+
error: {
|
|
87
|
+
border: "1px solid #c93232"
|
|
88
|
+
// destructive-500
|
|
89
|
+
},
|
|
90
|
+
errorFocus: {
|
|
91
|
+
borderColor: "#c93232",
|
|
92
|
+
outline: "3px solid rgba(201, 50, 50, 0.2)"
|
|
93
|
+
},
|
|
94
|
+
success: {
|
|
95
|
+
border: "1px solid #16a33d"
|
|
96
|
+
// success-500
|
|
97
|
+
},
|
|
98
|
+
successFocus: {
|
|
99
|
+
borderColor: "#16a33d",
|
|
100
|
+
outline: "3px solid rgba(22, 163, 61, 0.2)"
|
|
101
|
+
},
|
|
102
|
+
disabled: {
|
|
103
|
+
backgroundColor: "#f8f8f8",
|
|
104
|
+
// grey-050
|
|
105
|
+
borderColor: "#efefef",
|
|
106
|
+
// grey-100
|
|
107
|
+
cursor: "not-allowed"
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
var buttonStyles = {
|
|
112
|
+
width: "46px",
|
|
113
|
+
height: "100%",
|
|
114
|
+
border: "none",
|
|
115
|
+
background: "transparent",
|
|
116
|
+
cursor: "pointer",
|
|
117
|
+
fontSize: "16px",
|
|
118
|
+
color: "#2f2f2f",
|
|
119
|
+
display: "flex",
|
|
120
|
+
alignItems: "center",
|
|
121
|
+
justifyContent: "center",
|
|
122
|
+
transition: "background-color 0.2s",
|
|
123
|
+
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
|
124
|
+
userSelect: "none"
|
|
125
|
+
};
|
|
126
|
+
var inputStyles = {
|
|
127
|
+
flex: 1,
|
|
128
|
+
border: "none",
|
|
129
|
+
outline: "none",
|
|
130
|
+
background: "transparent",
|
|
131
|
+
textAlign: "center",
|
|
132
|
+
fontSize: "13px",
|
|
133
|
+
color: "#2f2f2f",
|
|
134
|
+
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
|
135
|
+
padding: "0 8px",
|
|
136
|
+
appearance: "textfield"
|
|
137
|
+
};
|
|
138
|
+
var NumericInput = React.forwardRef(
|
|
139
|
+
({
|
|
140
|
+
label,
|
|
141
|
+
value,
|
|
142
|
+
onChange,
|
|
143
|
+
min,
|
|
144
|
+
max,
|
|
145
|
+
step = 1,
|
|
146
|
+
state = "default",
|
|
147
|
+
error,
|
|
148
|
+
helperText,
|
|
149
|
+
disabled = false,
|
|
150
|
+
className,
|
|
151
|
+
style,
|
|
152
|
+
"data-testid": dataTestId
|
|
153
|
+
}, ref) => {
|
|
154
|
+
const [isFocused, setIsFocused] = React.useState(false);
|
|
155
|
+
const inputRef = React.useRef(null);
|
|
156
|
+
const inputId = React.useId();
|
|
157
|
+
const helperTextId = React.useId();
|
|
158
|
+
const errorId = React.useId();
|
|
159
|
+
const clampValue = (val) => {
|
|
160
|
+
let clamped = val;
|
|
161
|
+
if (min !== void 0 && clamped < min) clamped = min;
|
|
162
|
+
if (max !== void 0 && clamped > max) clamped = max;
|
|
163
|
+
return clamped;
|
|
164
|
+
};
|
|
165
|
+
const handleIncrement = () => {
|
|
166
|
+
if (disabled) return;
|
|
167
|
+
const currentValue = value ?? 0;
|
|
168
|
+
const newValue = currentValue + step;
|
|
169
|
+
const clampedValue = clampValue(newValue);
|
|
170
|
+
if (clampedValue !== currentValue) {
|
|
171
|
+
onChange?.(clampedValue);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const handleDecrement = () => {
|
|
175
|
+
if (disabled) return;
|
|
176
|
+
const currentValue = value ?? 0;
|
|
177
|
+
const newValue = currentValue - step;
|
|
178
|
+
const clampedValue = clampValue(newValue);
|
|
179
|
+
if (clampedValue !== currentValue) {
|
|
180
|
+
onChange?.(clampedValue);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const handleInputChange = (e) => {
|
|
184
|
+
const inputValue = e.target.value;
|
|
185
|
+
if (inputValue === "" || inputValue === "-") {
|
|
186
|
+
onChange?.(void 0);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const numValue = parseFloat(inputValue);
|
|
190
|
+
if (!isNaN(numValue)) {
|
|
191
|
+
const clampedValue = clampValue(numValue);
|
|
192
|
+
onChange?.(clampedValue);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
const handleKeyDown = (e) => {
|
|
196
|
+
const allowedKeys = ["Backspace", "Delete", "Tab", "Escape", "Enter", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "-", "."];
|
|
197
|
+
if (allowedKeys.includes(e.key)) {
|
|
198
|
+
if (e.key === "ArrowUp") {
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
handleIncrement();
|
|
201
|
+
} else if (e.key === "ArrowDown") {
|
|
202
|
+
e.preventDefault();
|
|
203
|
+
handleDecrement();
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (!/^[0-9]$/.test(e.key)) {
|
|
208
|
+
e.preventDefault();
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const currentState = error ? "error" : state;
|
|
212
|
+
const stateStyles = containerStyles.states[currentState];
|
|
213
|
+
const focusStyles = currentState === "error" ? containerStyles.states.errorFocus : currentState === "success" ? containerStyles.states.successFocus : containerStyles.states.defaultFocus;
|
|
214
|
+
const containerStyle = {
|
|
215
|
+
...containerStyles.base,
|
|
216
|
+
...stateStyles,
|
|
217
|
+
...isFocused && !disabled && focusStyles,
|
|
218
|
+
...disabled && containerStyles.states.disabled
|
|
219
|
+
};
|
|
220
|
+
const isDecrementDisabled = disabled || min !== void 0 && value !== void 0 && value <= min;
|
|
221
|
+
const isIncrementDisabled = disabled || max !== void 0 && value !== void 0 && value >= max;
|
|
222
|
+
const getButtonStyle = (isDisabled) => {
|
|
223
|
+
const baseStyle = {
|
|
224
|
+
...buttonStyles,
|
|
225
|
+
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
226
|
+
color: isDisabled ? "#7e7e7e" : "#2f2f2f"
|
|
227
|
+
};
|
|
228
|
+
if (currentState === "success" && !isDisabled) {
|
|
229
|
+
return {
|
|
230
|
+
...baseStyle,
|
|
231
|
+
color: "#16a33d",
|
|
232
|
+
// success-500
|
|
233
|
+
border: "2px solid #16a33d",
|
|
234
|
+
borderRadius: "50%",
|
|
235
|
+
width: "32px",
|
|
236
|
+
height: "32px"
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return baseStyle;
|
|
240
|
+
};
|
|
241
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: (0, import_clsx.clsx)("arbor-numeric-input-wrapper", className), style, ref, "data-testid": dataTestId, children: [
|
|
242
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: inputId, style: labelStyles, children: label }),
|
|
243
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: containerStyle, children: [
|
|
244
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
245
|
+
"button",
|
|
246
|
+
{
|
|
247
|
+
type: "button",
|
|
248
|
+
onClick: handleDecrement,
|
|
249
|
+
disabled: isDecrementDisabled,
|
|
250
|
+
style: getButtonStyle(isDecrementDisabled),
|
|
251
|
+
onMouseEnter: (e) => {
|
|
252
|
+
if (!isDecrementDisabled) {
|
|
253
|
+
e.currentTarget.style.backgroundColor = "#f8f8f8";
|
|
254
|
+
e.currentTarget.style.borderRadius = "99px";
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
onMouseLeave: (e) => {
|
|
258
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
259
|
+
e.currentTarget.style.borderRadius = currentState === "success" ? "50%" : "0";
|
|
260
|
+
},
|
|
261
|
+
"aria-label": "Decrement",
|
|
262
|
+
children: "\u2212"
|
|
263
|
+
}
|
|
264
|
+
),
|
|
265
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
266
|
+
"input",
|
|
267
|
+
{
|
|
268
|
+
ref: inputRef,
|
|
269
|
+
id: inputId,
|
|
270
|
+
type: "text",
|
|
271
|
+
inputMode: "numeric",
|
|
272
|
+
value: value !== void 0 ? value : "",
|
|
273
|
+
onChange: handleInputChange,
|
|
274
|
+
onKeyDown: handleKeyDown,
|
|
275
|
+
onFocus: () => setIsFocused(true),
|
|
276
|
+
onBlur: () => setIsFocused(false),
|
|
277
|
+
disabled,
|
|
278
|
+
"aria-invalid": error ? "true" : "false",
|
|
279
|
+
"aria-describedby": error ? errorId : helperText ? helperTextId : void 0,
|
|
280
|
+
style: {
|
|
281
|
+
...inputStyles,
|
|
282
|
+
color: disabled ? "#7e7e7e" : "#2f2f2f",
|
|
283
|
+
cursor: disabled ? "not-allowed" : "text"
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
),
|
|
287
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
288
|
+
"button",
|
|
289
|
+
{
|
|
290
|
+
type: "button",
|
|
291
|
+
onClick: handleIncrement,
|
|
292
|
+
disabled: isIncrementDisabled,
|
|
293
|
+
style: getButtonStyle(isIncrementDisabled),
|
|
294
|
+
onMouseEnter: (e) => {
|
|
295
|
+
if (!isIncrementDisabled) {
|
|
296
|
+
e.currentTarget.style.backgroundColor = "#f8f8f8";
|
|
297
|
+
e.currentTarget.style.borderRadius = "99px";
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
onMouseLeave: (e) => {
|
|
301
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
302
|
+
e.currentTarget.style.borderRadius = currentState === "success" ? "50%" : "0";
|
|
303
|
+
},
|
|
304
|
+
"aria-label": "Increment",
|
|
305
|
+
children: "+"
|
|
306
|
+
}
|
|
307
|
+
)
|
|
308
|
+
] }),
|
|
309
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { id: errorId, style: errorTextStyles, children: error }),
|
|
310
|
+
helperText && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { id: helperTextId, style: helperTextStyles, children: helperText })
|
|
311
|
+
] });
|
|
312
|
+
}
|
|
313
|
+
);
|
|
314
|
+
NumericInput.displayName = "NumericInput";
|
|
315
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
316
|
+
0 && (module.exports = {
|
|
317
|
+
NumericInput
|
|
318
|
+
});
|
|
319
|
+
//# sourceMappingURL=NumericInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/NumericInput/index.ts","../src/NumericInput/NumericInput.tsx"],"sourcesContent":["export { NumericInput } from './NumericInput';\nexport type { NumericInputProps, NumericInputState } from './NumericInput';\n","import * as React from 'react';\nimport { clsx } from 'clsx';\n\nexport type NumericInputState = 'default' | 'error' | 'success';\n\nexport interface NumericInputProps {\n /**\n * The label for the numeric input\n */\n label?: string;\n /**\n * The current value\n */\n value?: number;\n /**\n * Callback when value changes\n */\n onChange?: (value: number | undefined) => void;\n /**\n * Minimum allowed value\n */\n min?: number;\n /**\n * Maximum allowed value\n */\n max?: number;\n /**\n * Step increment/decrement value\n * @default 1\n */\n step?: number;\n /**\n * The validation state\n * @default 'default'\n */\n state?: NumericInputState;\n /**\n * Optional error message\n */\n error?: string;\n /**\n * Optional helper text\n */\n helperText?: string;\n /**\n * Whether the input is disabled\n */\n disabled?: boolean;\n /**\n * Custom className\n */\n className?: string;\n /**\n * Custom style\n */\n style?: React.CSSProperties;\n /**\n * Test ID for testing\n */\n 'data-testid'?: string;\n}\n\nconst labelStyles: React.CSSProperties = {\n display: 'block',\n fontSize: '13px',\n fontWeight: '600',\n color: '#2f2f2f',\n marginBottom: '4px',\n fontFamily: \"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n};\n\nconst helperTextStyles: React.CSSProperties = {\n fontSize: '13px',\n margin: '0',\n marginTop: '2px',\n color: '#595959',\n fontFamily: \"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n lineHeight: '1.4',\n};\n\nconst errorTextStyles: React.CSSProperties = {\n ...helperTextStyles,\n color: '#a62323', // destructive-600\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n};\n\nconst containerStyles = {\n base: {\n display: 'flex',\n alignItems: 'center',\n height: '46px',\n fontFamily: \"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n backgroundColor: '#ffffff',\n borderRadius: '8px',\n transition: 'all 0.2s ease-in-out',\n overflow: 'hidden',\n },\n states: {\n default: {\n border: '1px solid #d1d1d1', // grey-300\n },\n defaultFocus: {\n borderColor: '#3cad51', // brand-500\n outline: '3px solid rgba(60, 173, 81, 0.2)',\n },\n error: {\n border: '1px solid #c93232', // destructive-500\n },\n errorFocus: {\n borderColor: '#c93232',\n outline: '3px solid rgba(201, 50, 50, 0.2)',\n },\n success: {\n border: '1px solid #16a33d', // success-500\n },\n successFocus: {\n borderColor: '#16a33d',\n outline: '3px solid rgba(22, 163, 61, 0.2)',\n },\n disabled: {\n backgroundColor: '#f8f8f8', // grey-050\n borderColor: '#efefef', // grey-100\n cursor: 'not-allowed',\n },\n },\n};\n\nconst buttonStyles: React.CSSProperties = {\n width: '46px',\n height: '100%',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n fontSize: '16px',\n color: '#2f2f2f',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n transition: 'background-color 0.2s',\n fontFamily: \"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n userSelect: 'none' as const,\n};\n\nconst inputStyles: React.CSSProperties = {\n flex: 1,\n border: 'none',\n outline: 'none',\n background: 'transparent',\n textAlign: 'center',\n fontSize: '13px',\n color: '#2f2f2f',\n fontFamily: \"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n padding: '0 8px',\n appearance: 'textfield' as any,\n};\n\n/**\n * NumericInput component - Arbor Design System\n *\n * A number input with increment/decrement buttons.\n * Enforces numeric rules (min, max, step) and prevents non-numeric input.\n */\nexport const NumericInput = React.forwardRef<HTMLDivElement, NumericInputProps>(\n (\n {\n label,\n value,\n onChange,\n min,\n max,\n step = 1,\n state = 'default',\n error,\n helperText,\n disabled = false,\n className,\n style,\n 'data-testid': dataTestId,\n },\n ref\n ) => {\n const [isFocused, setIsFocused] = React.useState(false);\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n const inputId = React.useId();\n const helperTextId = React.useId();\n const errorId = React.useId();\n\n const clampValue = (val: number): number => {\n let clamped = val;\n if (min !== undefined && clamped < min) clamped = min;\n if (max !== undefined && clamped > max) clamped = max;\n return clamped;\n };\n\n const handleIncrement = () => {\n if (disabled) return;\n const currentValue = value ?? 0;\n const newValue = currentValue + step;\n const clampedValue = clampValue(newValue);\n if (clampedValue !== currentValue) {\n onChange?.(clampedValue);\n }\n };\n\n const handleDecrement = () => {\n if (disabled) return;\n const currentValue = value ?? 0;\n const newValue = currentValue - step;\n const clampedValue = clampValue(newValue);\n if (clampedValue !== currentValue) {\n onChange?.(clampedValue);\n }\n };\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const inputValue = e.target.value;\n\n // Allow empty input\n if (inputValue === '' || inputValue === '-') {\n onChange?.(undefined);\n return;\n }\n\n // Parse as number\n const numValue = parseFloat(inputValue);\n\n // Only update if it's a valid number\n if (!isNaN(numValue)) {\n const clampedValue = clampValue(numValue);\n onChange?.(clampedValue);\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n // Allow: backspace, delete, tab, escape, enter, minus sign, decimal point\n const allowedKeys = ['Backspace', 'Delete', 'Tab', 'Escape', 'Enter', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', '-', '.'];\n\n if (allowedKeys.includes(e.key)) {\n // Handle arrow keys for increment/decrement\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n handleIncrement();\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n handleDecrement();\n }\n return;\n }\n\n // Only allow numbers\n if (!/^[0-9]$/.test(e.key)) {\n e.preventDefault();\n }\n };\n\n const currentState = error ? 'error' : state;\n const stateStyles = containerStyles.states[currentState];\n const focusStyles =\n currentState === 'error'\n ? containerStyles.states.errorFocus\n : currentState === 'success'\n ? containerStyles.states.successFocus\n : containerStyles.states.defaultFocus;\n\n const containerStyle: React.CSSProperties = {\n ...containerStyles.base,\n ...stateStyles,\n ...(isFocused && !disabled && focusStyles),\n ...(disabled && containerStyles.states.disabled),\n };\n\n const isDecrementDisabled = disabled || (min !== undefined && value !== undefined && value <= min);\n const isIncrementDisabled = disabled || (max !== undefined && value !== undefined && value >= max);\n\n // Button styles for success state (green outlined circles)\n const getButtonStyle = (isDisabled: boolean) => {\n const baseStyle: React.CSSProperties = {\n ...buttonStyles,\n cursor: isDisabled ? 'not-allowed' : 'pointer',\n color: isDisabled ? '#7e7e7e' : '#2f2f2f',\n };\n\n // Add green circle border for success state\n if (currentState === 'success' && !isDisabled) {\n return {\n ...baseStyle,\n color: '#16a33d', // success-500\n border: '2px solid #16a33d',\n borderRadius: '50%',\n width: '32px',\n height: '32px',\n };\n }\n\n return baseStyle;\n };\n\n return (\n <div className={clsx('arbor-numeric-input-wrapper', className)} style={style} ref={ref} data-testid={dataTestId}>\n {label && (\n <label htmlFor={inputId} style={labelStyles}>\n {label}\n </label>\n )}\n\n <div style={containerStyle}>\n <button\n type=\"button\"\n onClick={handleDecrement}\n disabled={isDecrementDisabled}\n style={getButtonStyle(isDecrementDisabled)}\n onMouseEnter={(e) => {\n if (!isDecrementDisabled) {\n e.currentTarget.style.backgroundColor = '#f8f8f8';\n e.currentTarget.style.borderRadius = '99px';\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent';\n e.currentTarget.style.borderRadius = currentState === 'success' ? '50%' : '0';\n }}\n aria-label=\"Decrement\"\n >\n −\n </button>\n\n <input\n ref={inputRef}\n id={inputId}\n type=\"text\"\n inputMode=\"numeric\"\n value={value !== undefined ? value : ''}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n disabled={disabled}\n aria-invalid={error ? 'true' : 'false'}\n aria-describedby={error ? errorId : helperText ? helperTextId : undefined}\n style={{\n ...inputStyles,\n color: disabled ? '#7e7e7e' : '#2f2f2f',\n cursor: disabled ? 'not-allowed' : 'text',\n }}\n />\n\n <button\n type=\"button\"\n onClick={handleIncrement}\n disabled={isIncrementDisabled}\n style={getButtonStyle(isIncrementDisabled)}\n onMouseEnter={(e) => {\n if (!isIncrementDisabled) {\n e.currentTarget.style.backgroundColor = '#f8f8f8';\n e.currentTarget.style.borderRadius = '99px';\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent';\n e.currentTarget.style.borderRadius = currentState === 'success' ? '50%' : '0';\n }}\n aria-label=\"Increment\"\n >\n +\n </button>\n </div>\n\n {error && (\n <p id={errorId} style={errorTextStyles}>\n {error}\n </p>\n )}\n\n {helperText && !error && (\n <p id={helperTextId} style={helperTextStyles}>\n {helperText}\n </p>\n )}\n </div>\n );\n }\n);\n\nNumericInput.displayName = 'NumericInput';\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/Radio.d.mts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface RadioProps {
|
|
4
|
+
/**
|
|
5
|
+
* The label for the radio button
|
|
6
|
+
*/
|
|
7
|
+
label?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Whether the radio is selected
|
|
10
|
+
*/
|
|
11
|
+
checked?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Callback when selection changes
|
|
14
|
+
*/
|
|
15
|
+
onChange?: (checked: boolean) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Whether the radio is disabled
|
|
18
|
+
*/
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Custom className
|
|
22
|
+
*/
|
|
23
|
+
className?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Custom style
|
|
26
|
+
*/
|
|
27
|
+
style?: React.CSSProperties;
|
|
28
|
+
/**
|
|
29
|
+
* Test ID for testing
|
|
30
|
+
*/
|
|
31
|
+
'data-testid'?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Name attribute for form submission (groups radios together)
|
|
34
|
+
*/
|
|
35
|
+
name?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Value attribute for form submission
|
|
38
|
+
*/
|
|
39
|
+
value?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Radio component - Arbor Design System
|
|
43
|
+
*
|
|
44
|
+
* A radio button input with label support.
|
|
45
|
+
*/
|
|
46
|
+
declare const Radio: React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLInputElement>>;
|
|
47
|
+
|
|
48
|
+
export { Radio, type RadioProps };
|
package/dist/Radio.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface RadioProps {
|
|
4
|
+
/**
|
|
5
|
+
* The label for the radio button
|
|
6
|
+
*/
|
|
7
|
+
label?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Whether the radio is selected
|
|
10
|
+
*/
|
|
11
|
+
checked?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Callback when selection changes
|
|
14
|
+
*/
|
|
15
|
+
onChange?: (checked: boolean) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Whether the radio is disabled
|
|
18
|
+
*/
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Custom className
|
|
22
|
+
*/
|
|
23
|
+
className?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Custom style
|
|
26
|
+
*/
|
|
27
|
+
style?: React.CSSProperties;
|
|
28
|
+
/**
|
|
29
|
+
* Test ID for testing
|
|
30
|
+
*/
|
|
31
|
+
'data-testid'?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Name attribute for form submission (groups radios together)
|
|
34
|
+
*/
|
|
35
|
+
name?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Value attribute for form submission
|
|
38
|
+
*/
|
|
39
|
+
value?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Radio component - Arbor Design System
|
|
43
|
+
*
|
|
44
|
+
* A radio button input with label support.
|
|
45
|
+
*/
|
|
46
|
+
declare const Radio: React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLInputElement>>;
|
|
47
|
+
|
|
48
|
+
export { Radio, type RadioProps };
|
package/dist/Radio.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
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/Radio/index.ts
|
|
31
|
+
var Radio_exports = {};
|
|
32
|
+
__export(Radio_exports, {
|
|
33
|
+
Radio: () => Radio
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(Radio_exports);
|
|
36
|
+
|
|
37
|
+
// src/Radio/Radio.tsx
|
|
38
|
+
var React = __toESM(require("react"));
|
|
39
|
+
var import_clsx = require("clsx");
|
|
40
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
41
|
+
var radioStyles = {
|
|
42
|
+
width: "20px",
|
|
43
|
+
height: "20px",
|
|
44
|
+
border: "2px solid #d1d1d1",
|
|
45
|
+
// grey-300
|
|
46
|
+
borderRadius: "50%",
|
|
47
|
+
display: "flex",
|
|
48
|
+
alignItems: "center",
|
|
49
|
+
justifyContent: "center",
|
|
50
|
+
cursor: "pointer",
|
|
51
|
+
transition: "all 0.2s ease-in-out",
|
|
52
|
+
flexShrink: 0,
|
|
53
|
+
backgroundColor: "#ffffff"
|
|
54
|
+
};
|
|
55
|
+
var checkedStyles = {
|
|
56
|
+
borderColor: "#3cad51"
|
|
57
|
+
// brand-500
|
|
58
|
+
};
|
|
59
|
+
var innerDotStyles = {
|
|
60
|
+
width: "10px",
|
|
61
|
+
height: "10px",
|
|
62
|
+
borderRadius: "50%",
|
|
63
|
+
backgroundColor: "#3cad51",
|
|
64
|
+
// brand-500
|
|
65
|
+
transition: "all 0.2s ease-in-out"
|
|
66
|
+
};
|
|
67
|
+
var labelStyles = {
|
|
68
|
+
fontSize: "13px",
|
|
69
|
+
color: "#2f2f2f",
|
|
70
|
+
cursor: "pointer",
|
|
71
|
+
userSelect: "none",
|
|
72
|
+
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"
|
|
73
|
+
};
|
|
74
|
+
var wrapperStyles = {
|
|
75
|
+
display: "flex",
|
|
76
|
+
alignItems: "center",
|
|
77
|
+
gap: "8px"
|
|
78
|
+
};
|
|
79
|
+
var Radio = React.forwardRef(
|
|
80
|
+
({
|
|
81
|
+
label,
|
|
82
|
+
checked = false,
|
|
83
|
+
onChange,
|
|
84
|
+
disabled = false,
|
|
85
|
+
className,
|
|
86
|
+
style,
|
|
87
|
+
"data-testid": dataTestId,
|
|
88
|
+
name,
|
|
89
|
+
value
|
|
90
|
+
}, ref) => {
|
|
91
|
+
const [isFocused, setIsFocused] = React.useState(false);
|
|
92
|
+
const radioId = React.useId();
|
|
93
|
+
const handleChange = (e) => {
|
|
94
|
+
if (!disabled) {
|
|
95
|
+
onChange?.(e.target.checked);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const circleStyle = {
|
|
99
|
+
...radioStyles,
|
|
100
|
+
...checked && !disabled && checkedStyles,
|
|
101
|
+
...disabled && !checked && { backgroundColor: "#f8f8f8", borderColor: "#efefef" },
|
|
102
|
+
...disabled && checked && { borderColor: "#7e7e7e" },
|
|
103
|
+
...isFocused && !disabled && { borderColor: "#3cad51", outline: "2px solid rgba(60, 173, 81, 0.2)" }
|
|
104
|
+
};
|
|
105
|
+
const dotStyle = {
|
|
106
|
+
...innerDotStyles,
|
|
107
|
+
backgroundColor: disabled ? "#7e7e7e" : "#3cad51"
|
|
108
|
+
};
|
|
109
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
110
|
+
"div",
|
|
111
|
+
{
|
|
112
|
+
className: (0, import_clsx.clsx)("arbor-radio-wrapper", className),
|
|
113
|
+
style: { ...wrapperStyles, ...style },
|
|
114
|
+
"data-testid": dataTestId,
|
|
115
|
+
children: [
|
|
116
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
117
|
+
"input",
|
|
118
|
+
{
|
|
119
|
+
ref,
|
|
120
|
+
id: radioId,
|
|
121
|
+
type: "radio",
|
|
122
|
+
checked,
|
|
123
|
+
onChange: handleChange,
|
|
124
|
+
onFocus: () => setIsFocused(true),
|
|
125
|
+
onBlur: () => setIsFocused(false),
|
|
126
|
+
disabled,
|
|
127
|
+
name,
|
|
128
|
+
value,
|
|
129
|
+
style: {
|
|
130
|
+
position: "absolute",
|
|
131
|
+
opacity: 0,
|
|
132
|
+
width: 0,
|
|
133
|
+
height: 0
|
|
134
|
+
},
|
|
135
|
+
"aria-checked": checked
|
|
136
|
+
}
|
|
137
|
+
),
|
|
138
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
139
|
+
"label",
|
|
140
|
+
{
|
|
141
|
+
htmlFor: radioId,
|
|
142
|
+
style: {
|
|
143
|
+
display: "flex",
|
|
144
|
+
alignItems: "center",
|
|
145
|
+
gap: "8px",
|
|
146
|
+
cursor: disabled ? "not-allowed" : "pointer"
|
|
147
|
+
},
|
|
148
|
+
children: [
|
|
149
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: circleStyle, children: checked && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: dotStyle }) }),
|
|
150
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
151
|
+
"span",
|
|
152
|
+
{
|
|
153
|
+
style: {
|
|
154
|
+
...labelStyles,
|
|
155
|
+
color: disabled ? "#7e7e7e" : "#2f2f2f",
|
|
156
|
+
cursor: disabled ? "not-allowed" : "pointer"
|
|
157
|
+
},
|
|
158
|
+
children: label
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
Radio.displayName = "Radio";
|
|
170
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
171
|
+
0 && (module.exports = {
|
|
172
|
+
Radio
|
|
173
|
+
});
|
|
174
|
+
//# sourceMappingURL=Radio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/Radio/index.ts","../src/Radio/Radio.tsx"],"sourcesContent":["export { Radio } from './Radio';\nexport type { RadioProps } from './Radio';\n","import * as React from 'react';\nimport { clsx } from 'clsx';\n\nexport interface RadioProps {\n /**\n * The label for the radio button\n */\n label?: string;\n /**\n * Whether the radio is selected\n */\n checked?: boolean;\n /**\n * Callback when selection changes\n */\n onChange?: (checked: boolean) => void;\n /**\n * Whether the radio is disabled\n */\n disabled?: boolean;\n /**\n * Custom className\n */\n className?: string;\n /**\n * Custom style\n */\n style?: React.CSSProperties;\n /**\n * Test ID for testing\n */\n 'data-testid'?: string;\n /**\n * Name attribute for form submission (groups radios together)\n */\n name?: string;\n /**\n * Value attribute for form submission\n */\n value?: string;\n}\n\nconst radioStyles: React.CSSProperties = {\n width: '20px',\n height: '20px',\n border: '2px solid #d1d1d1', // grey-300\n borderRadius: '50%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n cursor: 'pointer',\n transition: 'all 0.2s ease-in-out',\n flexShrink: 0,\n backgroundColor: '#ffffff',\n};\n\nconst checkedStyles: React.CSSProperties = {\n borderColor: '#3cad51', // brand-500\n};\n\nconst innerDotStyles: React.CSSProperties = {\n width: '10px',\n height: '10px',\n borderRadius: '50%',\n backgroundColor: '#3cad51', // brand-500\n transition: 'all 0.2s ease-in-out',\n};\n\nconst labelStyles: React.CSSProperties = {\n fontSize: '13px',\n color: '#2f2f2f',\n cursor: 'pointer',\n userSelect: 'none' as const,\n fontFamily: \"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n};\n\nconst wrapperStyles: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n};\n\n/**\n * Radio component - Arbor Design System\n *\n * A radio button input with label support.\n */\nexport const Radio = React.forwardRef<HTMLInputElement, RadioProps>(\n (\n {\n label,\n checked = false,\n onChange,\n disabled = false,\n className,\n style,\n 'data-testid': dataTestId,\n name,\n value,\n },\n ref\n ) => {\n const [isFocused, setIsFocused] = React.useState(false);\n const radioId = React.useId();\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n if (!disabled) {\n onChange?.(e.target.checked);\n }\n };\n\n const circleStyle: React.CSSProperties = {\n ...radioStyles,\n ...(checked && !disabled && checkedStyles),\n ...(disabled && !checked && { backgroundColor: '#f8f8f8', borderColor: '#efefef' }),\n ...(disabled && checked && { borderColor: '#7e7e7e' }),\n ...(isFocused && !disabled && { borderColor: '#3cad51', outline: '2px solid rgba(60, 173, 81, 0.2)' }),\n };\n\n const dotStyle: React.CSSProperties = {\n ...innerDotStyles,\n backgroundColor: disabled ? '#7e7e7e' : '#3cad51',\n };\n\n return (\n <div\n className={clsx('arbor-radio-wrapper', className)}\n style={{ ...wrapperStyles, ...style }}\n data-testid={dataTestId}\n >\n <input\n ref={ref}\n id={radioId}\n type=\"radio\"\n checked={checked}\n onChange={handleChange}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n disabled={disabled}\n name={name}\n value={value}\n style={{\n position: 'absolute',\n opacity: 0,\n width: 0,\n height: 0,\n }}\n aria-checked={checked}\n />\n <label\n htmlFor={radioId}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n cursor: disabled ? 'not-allowed' : 'pointer',\n }}\n >\n <div style={circleStyle}>\n {checked && <div style={dotStyle} />}\n </div>\n {label && (\n <span\n style={{\n ...labelStyles,\n color: disabled ? '#7e7e7e' : '#2f2f2f',\n cursor: disabled ? 'not-allowed' : 'pointer',\n }}\n >\n {label}\n </span>\n )}\n </label>\n </div>\n );\n }\n);\n\nRadio.displayName = 'Radio';\n"],"mappings":"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","names":[]}
|
package/dist/Radio.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|