@alien-id/ui-kit-react 0.1.1

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/index.mjs ADDED
@@ -0,0 +1,406 @@
1
+ import { Drawer } from "vaul-base";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { memo, useCallback, useId, useRef, useState } from "react";
4
+ import { VisArea, VisXYContainer } from "@unovis/react";
5
+ import { CurveType } from "@unovis/ts";
6
+
7
+ //#region src/components/bottom-sheet/index.tsx
8
+ const BottomSheetComponent = ({ renderTrigger, children, open, onOpenChange, dismissible }) => {
9
+ return /* @__PURE__ */ jsxs(Drawer.Root, {
10
+ open,
11
+ onOpenChange,
12
+ dismissible,
13
+ children: [renderTrigger && /* @__PURE__ */ jsx(Drawer.Trigger, { render: renderTrigger }), /* @__PURE__ */ jsxs(Drawer.Portal, { children: [/* @__PURE__ */ jsx(Drawer.Overlay, { className: "alien-bottomsheet-overlay" }), /* @__PURE__ */ jsx(Drawer.Content, {
14
+ className: "alien-bottomsheet-content",
15
+ children: /* @__PURE__ */ jsxs("div", {
16
+ className: "alien-bottomsheet-inner",
17
+ children: [/* @__PURE__ */ jsx(Drawer.Handle, { className: "alien-bottomsheet-handle" }), children]
18
+ })
19
+ })] })]
20
+ });
21
+ };
22
+ BottomSheetComponent.displayName = "BottomSheet";
23
+ const Close = Drawer.Close;
24
+ Close.displayName = "BottomSheet.Close";
25
+ const Title = Drawer.Title;
26
+ Title.displayName = "BottomSheet.Title";
27
+ const Description = Drawer.Description;
28
+ Description.displayName = "BottomSheet.Description";
29
+ const BottomSheet = Object.assign(BottomSheetComponent, {
30
+ Close,
31
+ Title,
32
+ Description
33
+ });
34
+
35
+ //#endregion
36
+ //#region src/components/buttons/button.tsx
37
+ const Button = ({ children, variant = "primary", className, ...props }) => {
38
+ return /* @__PURE__ */ jsx("button", {
39
+ type: "button",
40
+ className: [
41
+ "alien-button",
42
+ `alien-button-${variant}`,
43
+ className
44
+ ].filter(Boolean).join(" "),
45
+ ...props,
46
+ children
47
+ });
48
+ };
49
+
50
+ //#endregion
51
+ //#region src/components/buttons/index.tsx
52
+ const PrimaryButton = (props) => /* @__PURE__ */ jsx(Button, {
53
+ ...props,
54
+ variant: "primary"
55
+ });
56
+ const SecondaryButton = (props) => /* @__PURE__ */ jsx(Button, {
57
+ ...props,
58
+ variant: "secondary"
59
+ });
60
+ const TertiaryButton = (props) => /* @__PURE__ */ jsx(Button, {
61
+ ...props,
62
+ variant: "tertiary"
63
+ });
64
+ const TintedButton = (props) => /* @__PURE__ */ jsx(Button, {
65
+ ...props,
66
+ variant: "tinted"
67
+ });
68
+
69
+ //#endregion
70
+ //#region src/helpers/cn.ts
71
+ const cn = (...classes) => {
72
+ return classes.filter(Boolean).join(" ");
73
+ };
74
+
75
+ //#endregion
76
+ //#region src/components/charts/const.ts
77
+ const DEFAULT_COLOR = "#2979FF";
78
+ const PERIOD_LABELS = {
79
+ "1H": "Today",
80
+ "1D": "Today",
81
+ "1W": "This week",
82
+ "1M": "This month"
83
+ };
84
+ const PERIOD_TIME_FORMAT = {
85
+ "1H": {
86
+ hour: "2-digit",
87
+ minute: "2-digit"
88
+ },
89
+ "1D": {
90
+ hour: "2-digit",
91
+ minute: "2-digit"
92
+ },
93
+ "1W": {
94
+ month: "short",
95
+ day: "numeric"
96
+ },
97
+ "1M": {
98
+ month: "short",
99
+ day: "numeric"
100
+ }
101
+ };
102
+
103
+ //#endregion
104
+ //#region src/components/charts/helpers.ts
105
+ function formatTimestamp(timestamp, period) {
106
+ if (typeof timestamp === "number") return String(timestamp);
107
+ const date = new Date(timestamp);
108
+ if (Number.isNaN(date.getTime())) return String(timestamp);
109
+ const opts = period ? PERIOD_TIME_FORMAT[period] : void 0;
110
+ return date.toLocaleString(void 0, opts);
111
+ }
112
+ const defaultFormatValue = (v) => v < .01 && v > 0 ? v.toFixed(6) : v.toFixed(2);
113
+
114
+ //#endregion
115
+ //#region src/components/charts/index.tsx
116
+ const PriceChart = memo(({ data, height = 200, label, currency, activePeriod: controlledPeriod, periods = [
117
+ "1H",
118
+ "1D",
119
+ "1W",
120
+ "1M"
121
+ ], onPeriodChange, onDataPointClick, formatValue = defaultFormatValue, color = DEFAULT_COLOR, className, showPeriodChange = true }) => {
122
+ const gradientId = `price-chart-gradient-${useId().replace(/:/g, "")}`;
123
+ const [internalPeriod, setInternalPeriod] = useState("1D");
124
+ const activePeriod = controlledPeriod ?? internalPeriod;
125
+ const lastHoveredDatum = useRef(null);
126
+ const chartRef = useRef(null);
127
+ const overlayRef = useRef(null);
128
+ const dotRef = useRef(null);
129
+ const crosshairRef = useRef(null);
130
+ const valueRef = useRef(null);
131
+ const changeRef = useRef(null);
132
+ const periodLabelRef = useRef(null);
133
+ const handlePeriodChange = useCallback((period) => {
134
+ setInternalPeriod(period);
135
+ onPeriodChange?.(period);
136
+ }, [onPeriodChange]);
137
+ const x = useCallback((_d, i) => i, []);
138
+ const y = useCallback((d) => d.value, []);
139
+ const lastPoint = data.length ? data[data.length - 1] : null;
140
+ const firstValue = data[0]?.value ?? 0;
141
+ const getDisplayInfo = useCallback((point) => {
142
+ const displayPoint = point ?? lastPoint;
143
+ const displayValue = displayPoint ? formatValue(displayPoint.value) : "—";
144
+ const changePercent = displayPoint && data.length >= 2 && firstValue !== 0 ? (displayPoint.value - firstValue) / firstValue * 100 : 0;
145
+ return {
146
+ displayValue,
147
+ changePercent,
148
+ isPositive: changePercent >= 0,
149
+ periodLabel: point ? formatTimestamp(point.timestamp, activePeriod) : PERIOD_LABELS[activePeriod] ?? activePeriod
150
+ };
151
+ }, [
152
+ lastPoint,
153
+ firstValue,
154
+ data.length,
155
+ formatValue,
156
+ activePeriod
157
+ ]);
158
+ const updateHeader = useCallback((point) => {
159
+ const { displayValue, changePercent, isPositive, periodLabel } = getDisplayInfo(point);
160
+ if (valueRef.current) valueRef.current.textContent = `${displayValue} ${currency || ""}`;
161
+ if (changeRef.current) {
162
+ changeRef.current.textContent = `${isPositive ? "+" : ""}${changePercent.toFixed(2)}%`;
163
+ changeRef.current.className = cn("alien-price-chart-change-text", isPositive ? "alien-price-chart-change-text--positive" : "alien-price-chart-change-text--negative");
164
+ }
165
+ if (periodLabelRef.current) periodLabelRef.current.textContent = periodLabel;
166
+ }, [getDisplayInfo, currency]);
167
+ const handleMouseMove = useCallback((e) => {
168
+ const chart = chartRef.current;
169
+ if (!chart || data.length < 2) return;
170
+ const rect = chart.getBoundingClientRect();
171
+ const mouseX = e.clientX - rect.left;
172
+ const ratio = Math.max(0, Math.min(1, mouseX / rect.width));
173
+ const idx = Math.round(ratio * (data.length - 1));
174
+ const datum = data[idx];
175
+ if (!datum) return;
176
+ lastHoveredDatum.current = datum;
177
+ updateHeader(datum);
178
+ const snappedX = idx / (data.length - 1) * rect.width;
179
+ const line = crosshairRef.current;
180
+ if (line) {
181
+ line.style.left = `${snappedX}px`;
182
+ line.style.opacity = "1";
183
+ }
184
+ const overlay = overlayRef.current;
185
+ if (overlay) {
186
+ overlay.style.left = `${snappedX}px`;
187
+ overlay.style.width = `${rect.width - snappedX}px`;
188
+ overlay.style.opacity = "1";
189
+ }
190
+ const dot = dotRef.current;
191
+ if (dot) {
192
+ const maxValue = Math.max(...data.map((d) => d.value));
193
+ const yPixel = height - (height - 10) * (datum.value / (maxValue || 1));
194
+ dot.style.left = `${snappedX - 4}px`;
195
+ dot.style.top = `${yPixel - 4}px`;
196
+ dot.style.opacity = "1";
197
+ }
198
+ }, [
199
+ data,
200
+ height,
201
+ updateHeader
202
+ ]);
203
+ const handleMouseLeave = useCallback(() => {
204
+ lastHoveredDatum.current = null;
205
+ updateHeader(null);
206
+ const line = crosshairRef.current;
207
+ if (line) line.style.opacity = "0";
208
+ const overlay = overlayRef.current;
209
+ if (overlay) overlay.style.opacity = "0";
210
+ const dot = dotRef.current;
211
+ if (dot) dot.style.opacity = "0";
212
+ }, [updateHeader]);
213
+ const handleChartClick = useCallback(() => {
214
+ if (lastHoveredDatum.current && onDataPointClick) onDataPointClick(lastHoveredDatum.current);
215
+ }, [onDataPointClick]);
216
+ const handleChartKeyDown = useCallback((e) => {
217
+ if (e.key === "Enter" || e.key === " ") handleChartClick();
218
+ }, [handleChartClick]);
219
+ const initial = getDisplayInfo(null);
220
+ return /* @__PURE__ */ jsxs("div", {
221
+ className: cn("alien-price-chart", className),
222
+ children: [
223
+ /* @__PURE__ */ jsxs("div", {
224
+ className: "alien-price-chart-header",
225
+ children: [/* @__PURE__ */ jsxs("div", {
226
+ className: "alien-price-chart-info",
227
+ children: [/* @__PURE__ */ jsxs("span", {
228
+ ref: valueRef,
229
+ className: "alien-price-chart-value",
230
+ children: [
231
+ initial.displayValue,
232
+ " ",
233
+ currency || ""
234
+ ]
235
+ }), label && /* @__PURE__ */ jsx("span", {
236
+ className: "alien-price-chart-label",
237
+ children: label
238
+ })]
239
+ }), /* @__PURE__ */ jsxs("div", {
240
+ className: cn("alien-price-chart-stats", showPeriodChange ? "alien-price-chart-stats--end" : "alien-price-chart-stats--start"),
241
+ children: [showPeriodChange && /* @__PURE__ */ jsxs("div", {
242
+ className: "alien-price-chart-change",
243
+ children: [/* @__PURE__ */ jsxs("svg", {
244
+ width: "16",
245
+ height: "16",
246
+ viewBox: "0 0 20 20",
247
+ fill: "none",
248
+ "aria-hidden": "true",
249
+ children: [/* @__PURE__ */ jsx("circle", {
250
+ cx: "10",
251
+ cy: "10",
252
+ r: "10",
253
+ fill: initial.isPositive ? color : "#F43F5D",
254
+ fillOpacity: "0.16"
255
+ }), initial.isPositive ? /* @__PURE__ */ jsx("path", {
256
+ d: "M7 13L13 7M13 7H8M13 7V12",
257
+ stroke: color,
258
+ strokeWidth: "1.5",
259
+ strokeLinecap: "round",
260
+ strokeLinejoin: "round"
261
+ }) : /* @__PURE__ */ jsx("path", {
262
+ d: "M7 7L13 13M13 13H8M13 13V8",
263
+ stroke: "#F43F5D",
264
+ strokeWidth: "1.5",
265
+ strokeLinecap: "round",
266
+ strokeLinejoin: "round"
267
+ })]
268
+ }), /* @__PURE__ */ jsxs("span", {
269
+ ref: changeRef,
270
+ className: cn("alien-price-chart-change-text", initial.isPositive ? "alien-price-chart-change-text--positive" : "alien-price-chart-change-text--negative"),
271
+ children: [
272
+ initial.isPositive ? "+" : "",
273
+ initial.changePercent.toFixed(2),
274
+ "%"
275
+ ]
276
+ })]
277
+ }), /* @__PURE__ */ jsx("span", {
278
+ ref: periodLabelRef,
279
+ className: "alien-price-chart-period-label",
280
+ children: initial.periodLabel
281
+ })]
282
+ })]
283
+ }),
284
+ /* @__PURE__ */ jsxs("button", {
285
+ ref: chartRef,
286
+ className: "alien-price-chart-area",
287
+ type: "button",
288
+ tabIndex: 0,
289
+ onClick: handleChartClick,
290
+ onKeyDown: handleChartKeyDown,
291
+ onMouseMove: handleMouseMove,
292
+ onMouseLeave: handleMouseLeave,
293
+ children: [
294
+ /* @__PURE__ */ jsx(VisXYContainer, {
295
+ data,
296
+ height,
297
+ padding: {
298
+ top: 10,
299
+ bottom: 0,
300
+ left: 0,
301
+ right: 0
302
+ },
303
+ autoMargin: false,
304
+ margin: {
305
+ top: 0,
306
+ bottom: 0,
307
+ left: 0,
308
+ right: 0
309
+ },
310
+ svgDefs: `
311
+ <linearGradient id="${gradientId}" x1="0" y1="0" x2="0" y2="1">
312
+ <stop offset="0%" stop-color="${color}" stop-opacity="0.3"/>
313
+ <stop offset="100%" stop-color="${color}" stop-opacity="0.02"/>
314
+ </linearGradient>
315
+ `,
316
+ children: /* @__PURE__ */ jsx(VisArea, {
317
+ x,
318
+ y,
319
+ color: `url(#${gradientId})`,
320
+ curveType: CurveType.MonotoneX,
321
+ line: true,
322
+ lineColor: color,
323
+ lineWidth: 2.5,
324
+ opacity: 1
325
+ })
326
+ }),
327
+ /* @__PURE__ */ jsx("div", {
328
+ ref: crosshairRef,
329
+ className: "alien-price-chart-crosshair",
330
+ style: {
331
+ background: "rgba(141,141,141,0.24)",
332
+ opacity: 0,
333
+ transition: "none"
334
+ }
335
+ }),
336
+ /* @__PURE__ */ jsx("div", {
337
+ ref: overlayRef,
338
+ className: "alien-price-chart-overlay",
339
+ style: {
340
+ background: "rgba(0, 0, 0, 0.45)",
341
+ opacity: 0,
342
+ transition: "none"
343
+ }
344
+ }),
345
+ /* @__PURE__ */ jsx("div", {
346
+ ref: dotRef,
347
+ className: "alien-price-chart-dot",
348
+ style: {
349
+ background: color,
350
+ opacity: 0,
351
+ transition: "none"
352
+ }
353
+ })
354
+ ]
355
+ }),
356
+ periods.length > 0 && /* @__PURE__ */ jsx("div", {
357
+ className: "alien-price-chart-periods",
358
+ children: periods.map((period) => /* @__PURE__ */ jsx("button", {
359
+ type: "button",
360
+ onClick: () => handlePeriodChange(period),
361
+ className: cn("alien-price-chart-period-btn", activePeriod === period && "alien-price-chart-period-btn--active"),
362
+ children: period
363
+ }, period))
364
+ })
365
+ ]
366
+ });
367
+ });
368
+ PriceChart.displayName = "PriceChart";
369
+
370
+ //#endregion
371
+ //#region src/components/inputs/floating-label-input.tsx
372
+ function FloatingLabelInput({ id, label, className, disabled, ...props }) {
373
+ return /* @__PURE__ */ jsxs("div", {
374
+ className: "alien-floating-wrapper",
375
+ children: [/* @__PURE__ */ jsx("input", {
376
+ type: "text",
377
+ id,
378
+ className: [
379
+ "alien-floating-input",
380
+ disabled && "alien-floating-input-disabled",
381
+ className
382
+ ].filter(Boolean).join(" "),
383
+ placeholder: " ",
384
+ disabled,
385
+ ...props
386
+ }), /* @__PURE__ */ jsx("label", {
387
+ htmlFor: id,
388
+ className: "alien-floating-label",
389
+ children: label
390
+ })]
391
+ });
392
+ }
393
+
394
+ //#endregion
395
+ //#region src/components/inputs/input.tsx
396
+ function Input({ className, type, ...props }) {
397
+ return /* @__PURE__ */ jsx("input", {
398
+ type,
399
+ "data-slot": "input",
400
+ className: ["alien-input", className].filter(Boolean).join(" "),
401
+ ...props
402
+ });
403
+ }
404
+
405
+ //#endregion
406
+ export { BottomSheet, Button, FloatingLabelInput, Input, PriceChart, PrimaryButton, SecondaryButton, TertiaryButton, TintedButton };
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@alien-id/ui-kit-react",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.mts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.mts",
12
+ "default": "./dist/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.cts",
16
+ "default": "./dist/index.cjs"
17
+ }
18
+ },
19
+ "./styles.css": "./dist/index.css"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/alien-id/ui-kit-react.git"
27
+ },
28
+ "publishConfig": {
29
+ "registry": "https://registry.npmjs.org",
30
+ "access": "public"
31
+ },
32
+ "scripts": {
33
+ "build": "tsdown",
34
+ "prepublishOnly": "bun run build",
35
+ "storybook": "storybook dev -p 6006",
36
+ "build-storybook": "storybook build"
37
+ },
38
+ "devDependencies": {
39
+ "@types/bun": "^1.3.9",
40
+ "@types/react": "^19.2.0",
41
+ "react": "^19.2.4",
42
+ "tsdown": "^0.20.3",
43
+ "typescript": "^5",
44
+ "storybook": "^10.2.16",
45
+ "@storybook/react-vite": "^10.2.16",
46
+ "@chromatic-com/storybook": "^5.0.1",
47
+ "@storybook/addon-vitest": "^10.2.16",
48
+ "@storybook/addon-a11y": "^10.2.16",
49
+ "@storybook/addon-docs": "^10.2.16",
50
+ "@storybook/addon-onboarding": "^10.2.16",
51
+ "vitest": "^4.0.18",
52
+ "playwright": "^1.58.2",
53
+ "@vitest/browser-playwright": "^4.0.18",
54
+ "@vitest/coverage-v8": "^4.0.18"
55
+ },
56
+ "peerDependencies": {
57
+ "react": "^18 || ^19",
58
+ "react-dom": "^18 || ^19"
59
+ },
60
+ "dependencies": {
61
+ "@unovis/react": "^1.6.4",
62
+ "@unovis/ts": "^1.6.4",
63
+ "vaul-base": "^1.0.0"
64
+ }
65
+ }