@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/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # @alien-id/ui-kit-react
2
+
3
+ UI components for Alien miniapps.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @alien-id/ui-kit-react
9
+ # or
10
+ npm install @alien-id/ui-kit-react
11
+ ```
12
+
13
+ Import styles in your app entry:
14
+
15
+ ```tsx
16
+ import "@alien-id/ui-kit-react/styles.css";
17
+ ```
18
+
19
+ ## Storybook
20
+
21
+ Browse all components and their variants in the live Storybook:
22
+
23
+ **https://main.d1duwphdfo3vec.amplifyapp.com**
24
+
25
+ To run Storybook locally:
26
+
27
+ ```bash
28
+ npm run storybook
29
+ ```
30
+
31
+ ## Components
32
+
33
+ - **Buttons** — Primary, Secondary, Tertiary, Tinted variants
34
+ - **Input** — Basic text input
35
+ - **FloatingLabelInput** — Input with animated floating label
36
+ - **BottomSheet** — Draggable bottom sheet built on vaul-base
37
+ - **PriceChart** — Interactive area chart with period selection
38
+
39
+ See the [Storybook](https://main.d1duwphdfo3vec.amplifyapp.com) for live examples, props, and usage.
40
+
41
+ ## Theming
42
+
43
+ CSS custom properties are used for theming — see `styles/global.css` for available variables.
package/dist/index.cjs ADDED
@@ -0,0 +1,443 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) {
14
+ __defProp(to, key, {
15
+ get: ((k) => from[k]).bind(null, key),
16
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
+ });
18
+ }
19
+ }
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
24
+ value: mod,
25
+ enumerable: true
26
+ }) : target, mod));
27
+
28
+ //#endregion
29
+ let vaul_base = require("vaul-base");
30
+ let react_jsx_runtime = require("react/jsx-runtime");
31
+ let react = require("react");
32
+ react = __toESM(react);
33
+ let _unovis_react = require("@unovis/react");
34
+ let _unovis_ts = require("@unovis/ts");
35
+
36
+ //#region src/components/bottom-sheet/index.tsx
37
+ const BottomSheetComponent = ({ renderTrigger, children, open, onOpenChange, dismissible }) => {
38
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(vaul_base.Drawer.Root, {
39
+ open,
40
+ onOpenChange,
41
+ dismissible,
42
+ children: [renderTrigger && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(vaul_base.Drawer.Trigger, { render: renderTrigger }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(vaul_base.Drawer.Portal, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(vaul_base.Drawer.Overlay, { className: "alien-bottomsheet-overlay" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(vaul_base.Drawer.Content, {
43
+ className: "alien-bottomsheet-content",
44
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
45
+ className: "alien-bottomsheet-inner",
46
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(vaul_base.Drawer.Handle, { className: "alien-bottomsheet-handle" }), children]
47
+ })
48
+ })] })]
49
+ });
50
+ };
51
+ BottomSheetComponent.displayName = "BottomSheet";
52
+ const Close = vaul_base.Drawer.Close;
53
+ Close.displayName = "BottomSheet.Close";
54
+ const Title = vaul_base.Drawer.Title;
55
+ Title.displayName = "BottomSheet.Title";
56
+ const Description = vaul_base.Drawer.Description;
57
+ Description.displayName = "BottomSheet.Description";
58
+ const BottomSheet = Object.assign(BottomSheetComponent, {
59
+ Close,
60
+ Title,
61
+ Description
62
+ });
63
+
64
+ //#endregion
65
+ //#region src/components/buttons/button.tsx
66
+ const Button = ({ children, variant = "primary", className, ...props }) => {
67
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
68
+ type: "button",
69
+ className: [
70
+ "alien-button",
71
+ `alien-button-${variant}`,
72
+ className
73
+ ].filter(Boolean).join(" "),
74
+ ...props,
75
+ children
76
+ });
77
+ };
78
+
79
+ //#endregion
80
+ //#region src/components/buttons/index.tsx
81
+ const PrimaryButton = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
82
+ ...props,
83
+ variant: "primary"
84
+ });
85
+ const SecondaryButton = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
86
+ ...props,
87
+ variant: "secondary"
88
+ });
89
+ const TertiaryButton = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
90
+ ...props,
91
+ variant: "tertiary"
92
+ });
93
+ const TintedButton = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
94
+ ...props,
95
+ variant: "tinted"
96
+ });
97
+
98
+ //#endregion
99
+ //#region src/helpers/cn.ts
100
+ const cn = (...classes) => {
101
+ return classes.filter(Boolean).join(" ");
102
+ };
103
+
104
+ //#endregion
105
+ //#region src/components/charts/const.ts
106
+ const DEFAULT_COLOR = "#2979FF";
107
+ const PERIOD_LABELS = {
108
+ "1H": "Today",
109
+ "1D": "Today",
110
+ "1W": "This week",
111
+ "1M": "This month"
112
+ };
113
+ const PERIOD_TIME_FORMAT = {
114
+ "1H": {
115
+ hour: "2-digit",
116
+ minute: "2-digit"
117
+ },
118
+ "1D": {
119
+ hour: "2-digit",
120
+ minute: "2-digit"
121
+ },
122
+ "1W": {
123
+ month: "short",
124
+ day: "numeric"
125
+ },
126
+ "1M": {
127
+ month: "short",
128
+ day: "numeric"
129
+ }
130
+ };
131
+
132
+ //#endregion
133
+ //#region src/components/charts/helpers.ts
134
+ function formatTimestamp(timestamp, period) {
135
+ if (typeof timestamp === "number") return String(timestamp);
136
+ const date = new Date(timestamp);
137
+ if (Number.isNaN(date.getTime())) return String(timestamp);
138
+ const opts = period ? PERIOD_TIME_FORMAT[period] : void 0;
139
+ return date.toLocaleString(void 0, opts);
140
+ }
141
+ const defaultFormatValue = (v) => v < .01 && v > 0 ? v.toFixed(6) : v.toFixed(2);
142
+
143
+ //#endregion
144
+ //#region src/components/charts/index.tsx
145
+ const PriceChart = (0, react.memo)(({ data, height = 200, label, currency, activePeriod: controlledPeriod, periods = [
146
+ "1H",
147
+ "1D",
148
+ "1W",
149
+ "1M"
150
+ ], onPeriodChange, onDataPointClick, formatValue = defaultFormatValue, color = DEFAULT_COLOR, className, showPeriodChange = true }) => {
151
+ const gradientId = `price-chart-gradient-${(0, react.useId)().replace(/:/g, "")}`;
152
+ const [internalPeriod, setInternalPeriod] = (0, react.useState)("1D");
153
+ const activePeriod = controlledPeriod ?? internalPeriod;
154
+ const lastHoveredDatum = (0, react.useRef)(null);
155
+ const chartRef = (0, react.useRef)(null);
156
+ const overlayRef = (0, react.useRef)(null);
157
+ const dotRef = (0, react.useRef)(null);
158
+ const crosshairRef = (0, react.useRef)(null);
159
+ const valueRef = (0, react.useRef)(null);
160
+ const changeRef = (0, react.useRef)(null);
161
+ const periodLabelRef = (0, react.useRef)(null);
162
+ const handlePeriodChange = (0, react.useCallback)((period) => {
163
+ setInternalPeriod(period);
164
+ onPeriodChange?.(period);
165
+ }, [onPeriodChange]);
166
+ const x = (0, react.useCallback)((_d, i) => i, []);
167
+ const y = (0, react.useCallback)((d) => d.value, []);
168
+ const lastPoint = data.length ? data[data.length - 1] : null;
169
+ const firstValue = data[0]?.value ?? 0;
170
+ const getDisplayInfo = (0, react.useCallback)((point) => {
171
+ const displayPoint = point ?? lastPoint;
172
+ const displayValue = displayPoint ? formatValue(displayPoint.value) : "—";
173
+ const changePercent = displayPoint && data.length >= 2 && firstValue !== 0 ? (displayPoint.value - firstValue) / firstValue * 100 : 0;
174
+ return {
175
+ displayValue,
176
+ changePercent,
177
+ isPositive: changePercent >= 0,
178
+ periodLabel: point ? formatTimestamp(point.timestamp, activePeriod) : PERIOD_LABELS[activePeriod] ?? activePeriod
179
+ };
180
+ }, [
181
+ lastPoint,
182
+ firstValue,
183
+ data.length,
184
+ formatValue,
185
+ activePeriod
186
+ ]);
187
+ const updateHeader = (0, react.useCallback)((point) => {
188
+ const { displayValue, changePercent, isPositive, periodLabel } = getDisplayInfo(point);
189
+ if (valueRef.current) valueRef.current.textContent = `${displayValue} ${currency || ""}`;
190
+ if (changeRef.current) {
191
+ changeRef.current.textContent = `${isPositive ? "+" : ""}${changePercent.toFixed(2)}%`;
192
+ changeRef.current.className = cn("alien-price-chart-change-text", isPositive ? "alien-price-chart-change-text--positive" : "alien-price-chart-change-text--negative");
193
+ }
194
+ if (periodLabelRef.current) periodLabelRef.current.textContent = periodLabel;
195
+ }, [getDisplayInfo, currency]);
196
+ const handleMouseMove = (0, react.useCallback)((e) => {
197
+ const chart = chartRef.current;
198
+ if (!chart || data.length < 2) return;
199
+ const rect = chart.getBoundingClientRect();
200
+ const mouseX = e.clientX - rect.left;
201
+ const ratio = Math.max(0, Math.min(1, mouseX / rect.width));
202
+ const idx = Math.round(ratio * (data.length - 1));
203
+ const datum = data[idx];
204
+ if (!datum) return;
205
+ lastHoveredDatum.current = datum;
206
+ updateHeader(datum);
207
+ const snappedX = idx / (data.length - 1) * rect.width;
208
+ const line = crosshairRef.current;
209
+ if (line) {
210
+ line.style.left = `${snappedX}px`;
211
+ line.style.opacity = "1";
212
+ }
213
+ const overlay = overlayRef.current;
214
+ if (overlay) {
215
+ overlay.style.left = `${snappedX}px`;
216
+ overlay.style.width = `${rect.width - snappedX}px`;
217
+ overlay.style.opacity = "1";
218
+ }
219
+ const dot = dotRef.current;
220
+ if (dot) {
221
+ const maxValue = Math.max(...data.map((d) => d.value));
222
+ const yPixel = height - (height - 10) * (datum.value / (maxValue || 1));
223
+ dot.style.left = `${snappedX - 4}px`;
224
+ dot.style.top = `${yPixel - 4}px`;
225
+ dot.style.opacity = "1";
226
+ }
227
+ }, [
228
+ data,
229
+ height,
230
+ updateHeader
231
+ ]);
232
+ const handleMouseLeave = (0, react.useCallback)(() => {
233
+ lastHoveredDatum.current = null;
234
+ updateHeader(null);
235
+ const line = crosshairRef.current;
236
+ if (line) line.style.opacity = "0";
237
+ const overlay = overlayRef.current;
238
+ if (overlay) overlay.style.opacity = "0";
239
+ const dot = dotRef.current;
240
+ if (dot) dot.style.opacity = "0";
241
+ }, [updateHeader]);
242
+ const handleChartClick = (0, react.useCallback)(() => {
243
+ if (lastHoveredDatum.current && onDataPointClick) onDataPointClick(lastHoveredDatum.current);
244
+ }, [onDataPointClick]);
245
+ const handleChartKeyDown = (0, react.useCallback)((e) => {
246
+ if (e.key === "Enter" || e.key === " ") handleChartClick();
247
+ }, [handleChartClick]);
248
+ const initial = getDisplayInfo(null);
249
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
250
+ className: cn("alien-price-chart", className),
251
+ children: [
252
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
253
+ className: "alien-price-chart-header",
254
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
255
+ className: "alien-price-chart-info",
256
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
257
+ ref: valueRef,
258
+ className: "alien-price-chart-value",
259
+ children: [
260
+ initial.displayValue,
261
+ " ",
262
+ currency || ""
263
+ ]
264
+ }), label && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
265
+ className: "alien-price-chart-label",
266
+ children: label
267
+ })]
268
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
269
+ className: cn("alien-price-chart-stats", showPeriodChange ? "alien-price-chart-stats--end" : "alien-price-chart-stats--start"),
270
+ children: [showPeriodChange && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
271
+ className: "alien-price-chart-change",
272
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
273
+ width: "16",
274
+ height: "16",
275
+ viewBox: "0 0 20 20",
276
+ fill: "none",
277
+ "aria-hidden": "true",
278
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
279
+ cx: "10",
280
+ cy: "10",
281
+ r: "10",
282
+ fill: initial.isPositive ? color : "#F43F5D",
283
+ fillOpacity: "0.16"
284
+ }), initial.isPositive ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
285
+ d: "M7 13L13 7M13 7H8M13 7V12",
286
+ stroke: color,
287
+ strokeWidth: "1.5",
288
+ strokeLinecap: "round",
289
+ strokeLinejoin: "round"
290
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
291
+ d: "M7 7L13 13M13 13H8M13 13V8",
292
+ stroke: "#F43F5D",
293
+ strokeWidth: "1.5",
294
+ strokeLinecap: "round",
295
+ strokeLinejoin: "round"
296
+ })]
297
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
298
+ ref: changeRef,
299
+ className: cn("alien-price-chart-change-text", initial.isPositive ? "alien-price-chart-change-text--positive" : "alien-price-chart-change-text--negative"),
300
+ children: [
301
+ initial.isPositive ? "+" : "",
302
+ initial.changePercent.toFixed(2),
303
+ "%"
304
+ ]
305
+ })]
306
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
307
+ ref: periodLabelRef,
308
+ className: "alien-price-chart-period-label",
309
+ children: initial.periodLabel
310
+ })]
311
+ })]
312
+ }),
313
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
314
+ ref: chartRef,
315
+ className: "alien-price-chart-area",
316
+ type: "button",
317
+ tabIndex: 0,
318
+ onClick: handleChartClick,
319
+ onKeyDown: handleChartKeyDown,
320
+ onMouseMove: handleMouseMove,
321
+ onMouseLeave: handleMouseLeave,
322
+ children: [
323
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_unovis_react.VisXYContainer, {
324
+ data,
325
+ height,
326
+ padding: {
327
+ top: 10,
328
+ bottom: 0,
329
+ left: 0,
330
+ right: 0
331
+ },
332
+ autoMargin: false,
333
+ margin: {
334
+ top: 0,
335
+ bottom: 0,
336
+ left: 0,
337
+ right: 0
338
+ },
339
+ svgDefs: `
340
+ <linearGradient id="${gradientId}" x1="0" y1="0" x2="0" y2="1">
341
+ <stop offset="0%" stop-color="${color}" stop-opacity="0.3"/>
342
+ <stop offset="100%" stop-color="${color}" stop-opacity="0.02"/>
343
+ </linearGradient>
344
+ `,
345
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_unovis_react.VisArea, {
346
+ x,
347
+ y,
348
+ color: `url(#${gradientId})`,
349
+ curveType: _unovis_ts.CurveType.MonotoneX,
350
+ line: true,
351
+ lineColor: color,
352
+ lineWidth: 2.5,
353
+ opacity: 1
354
+ })
355
+ }),
356
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
357
+ ref: crosshairRef,
358
+ className: "alien-price-chart-crosshair",
359
+ style: {
360
+ background: "rgba(141,141,141,0.24)",
361
+ opacity: 0,
362
+ transition: "none"
363
+ }
364
+ }),
365
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
366
+ ref: overlayRef,
367
+ className: "alien-price-chart-overlay",
368
+ style: {
369
+ background: "rgba(0, 0, 0, 0.45)",
370
+ opacity: 0,
371
+ transition: "none"
372
+ }
373
+ }),
374
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
375
+ ref: dotRef,
376
+ className: "alien-price-chart-dot",
377
+ style: {
378
+ background: color,
379
+ opacity: 0,
380
+ transition: "none"
381
+ }
382
+ })
383
+ ]
384
+ }),
385
+ periods.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
386
+ className: "alien-price-chart-periods",
387
+ children: periods.map((period) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
388
+ type: "button",
389
+ onClick: () => handlePeriodChange(period),
390
+ className: cn("alien-price-chart-period-btn", activePeriod === period && "alien-price-chart-period-btn--active"),
391
+ children: period
392
+ }, period))
393
+ })
394
+ ]
395
+ });
396
+ });
397
+ PriceChart.displayName = "PriceChart";
398
+
399
+ //#endregion
400
+ //#region src/components/inputs/floating-label-input.tsx
401
+ function FloatingLabelInput({ id, label, className, disabled, ...props }) {
402
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
403
+ className: "alien-floating-wrapper",
404
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
405
+ type: "text",
406
+ id,
407
+ className: [
408
+ "alien-floating-input",
409
+ disabled && "alien-floating-input-disabled",
410
+ className
411
+ ].filter(Boolean).join(" "),
412
+ placeholder: " ",
413
+ disabled,
414
+ ...props
415
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
416
+ htmlFor: id,
417
+ className: "alien-floating-label",
418
+ children: label
419
+ })]
420
+ });
421
+ }
422
+
423
+ //#endregion
424
+ //#region src/components/inputs/input.tsx
425
+ function Input({ className, type, ...props }) {
426
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
427
+ type,
428
+ "data-slot": "input",
429
+ className: ["alien-input", className].filter(Boolean).join(" "),
430
+ ...props
431
+ });
432
+ }
433
+
434
+ //#endregion
435
+ exports.BottomSheet = BottomSheet;
436
+ exports.Button = Button;
437
+ exports.FloatingLabelInput = FloatingLabelInput;
438
+ exports.Input = Input;
439
+ exports.PriceChart = PriceChart;
440
+ exports.PrimaryButton = PrimaryButton;
441
+ exports.SecondaryButton = SecondaryButton;
442
+ exports.TertiaryButton = TertiaryButton;
443
+ exports.TintedButton = TintedButton;