@react-component-library-nlxx/components 1.0.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,652 @@
1
+ import React, { createContext, forwardRef, memo, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
2
+ import styled, { ThemeProvider as ThemeProvider$1, css } from "styled-components";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { Subject, fromEvent } from "rxjs";
5
+ import { debounceTime, distinctUntilChanged, filter, map, throttleTime } from "rxjs/operators";
6
+
7
+ //#region src/Button/style.ts
8
+ const sizeStyles = {
9
+ small: css`
10
+ height: 24px;
11
+ padding: 0 8px;
12
+ font-size: 12px;
13
+ `,
14
+ medium: css`
15
+ height: 32px;
16
+ padding: 0 16px;
17
+ font-size: 14px;
18
+ `,
19
+ large: css`
20
+ height: 40px;
21
+ padding: 0 24px;
22
+ font-size: 16px;
23
+ `
24
+ };
25
+ const variantStyles = {
26
+ primary: css`
27
+ background: #1890ff;
28
+ color: white;
29
+ border: none;
30
+ &:hover:not(:disabled) { background: #40a9ff; }
31
+ &:active:not(:disabled) { background: #096dd9; }
32
+ `,
33
+ secondary: css`
34
+ background: #f0f2f5;
35
+ color: #333;
36
+ border: none;
37
+ &:hover:not(:disabled) { background: #e6e9f0; }
38
+ `,
39
+ outline: css`
40
+ background: transparent;
41
+ color: #1890ff;
42
+ border: 1px solid #1890ff;
43
+ &:hover:not(:disabled) { background: #e6f7ff; }
44
+ `,
45
+ ghost: css`
46
+ background: transparent;
47
+ color: #1890ff;
48
+ border: none;
49
+ &:hover:not(:disabled) { background: rgba(24, 144, 255, 0.1); }
50
+ `
51
+ };
52
+ const shapeStyles = {
53
+ default: css`border-radius: 4px;`,
54
+ round: css`border-radius: 32px;`,
55
+ circle: css`
56
+ border-radius: 50%;
57
+ padding: 0;
58
+ width: ${(props) => {
59
+ const size = props.size || "medium";
60
+ return size === "small" ? "24px" : size === "medium" ? "32px" : "40px";
61
+ }};
62
+ `
63
+ };
64
+ const StyledButton = styled.button`
65
+ display: inline-flex;
66
+ align-items: center;
67
+ justify-content: center;
68
+ gap: 8px;
69
+ cursor: pointer;
70
+ transition: all 0.2s;
71
+ font-weight: 400;
72
+ white-space: nowrap;
73
+
74
+ /* 尺寸 */
75
+ ${({ size = "medium" }) => sizeStyles[size]}
76
+
77
+ /* 变体 */
78
+ ${({ variant = "primary" }) => variantStyles[variant]}
79
+
80
+ /* 形状 */
81
+ ${({ shape = "default" }) => shapeStyles[shape]}
82
+
83
+ /* 块级按钮 */
84
+ ${({ block }) => block && css`width: 100%;`}
85
+
86
+ /* 加载状态 */
87
+ ${({ loading }) => loading && css`
88
+ opacity: 0.7;
89
+ cursor: not-allowed;
90
+ pointer-events: none;
91
+ `}
92
+
93
+ /* 禁用状态 */
94
+ &:disabled {
95
+ opacity: 0.5;
96
+ cursor: not-allowed;
97
+ }
98
+
99
+ /* 图标样式 */
100
+ .btn-icon {
101
+ display: inline-flex;
102
+ align-items: center;
103
+ }
104
+ `;
105
+
106
+ //#endregion
107
+ //#region src/Button/index.tsx
108
+ const Button = forwardRef((props, ref) => {
109
+ const { children, size = "medium", variant = "primary", shape = "default", loading = false, disabled = false, block = false, icon, iconPosition = "left", type = "button", ...rest } = props;
110
+ const renderIcon = () => {
111
+ if (!icon) return null;
112
+ return /* @__PURE__ */ jsx("span", {
113
+ className: `btn-icon btn-icon-${iconPosition}`,
114
+ children: icon
115
+ });
116
+ };
117
+ return /* @__PURE__ */ jsxs(StyledButton, {
118
+ ref,
119
+ size,
120
+ variant,
121
+ shape,
122
+ loading,
123
+ disabled: disabled || loading,
124
+ block,
125
+ type,
126
+ ...rest,
127
+ children: [
128
+ iconPosition === "left" && renderIcon(),
129
+ children,
130
+ iconPosition === "right" && renderIcon()
131
+ ]
132
+ });
133
+ });
134
+ Button.displayName = "Button";
135
+
136
+ //#endregion
137
+ //#region src/Input/index.tsx
138
+ const ThemeContext$1 = React.createContext("light");
139
+ /**
140
+ * 非受控组件将状态交给 DOM 自己管理
141
+ * - 子组件:通过 forwardRef 接收 ref,并将其附加到内部的 input 元素,通过 ref 调用 clear()、直接操作 DOM(不推荐)。
142
+ *
143
+ * 受控组件将状态
144
+ * - 动态更新值,应采用受控模式(即使用 value 和 onChange)
145
+ */
146
+ const Input = memo(forwardRef((props, ref) => {
147
+ const { name, placeholder, value, onChange, disabled, clearable, onClear, ...rest } = props;
148
+ const inputRef = useRef(null);
149
+ const theme = useContext(ThemeContext$1);
150
+ useImperativeHandle(ref, () => ({
151
+ focus: () => inputRef.current?.focus(),
152
+ blur: () => inputRef.current?.blur(),
153
+ clear: () => {
154
+ if (inputRef.current) {
155
+ inputRef.current.value = "";
156
+ if (onChange) {
157
+ const event = new Event("input", { bubbles: true });
158
+ Object.defineProperty(event, "target", { value: { value: "" } });
159
+ onChange(event);
160
+ }
161
+ }
162
+ },
163
+ getValue: () => inputRef.current?.value
164
+ }));
165
+ /**
166
+ * 清除逻辑同时兼容受控和非受控模式:受控时调用 onChange 更新父组件状态;非受控时直接修改 DOM。
167
+ * 通过 onClear 可额外执行清除后的操作
168
+ */
169
+ const handleClear = () => {
170
+ if (onChange) {
171
+ const event = new Event("input", { bubbles: true });
172
+ Object.defineProperty(event, "target", {
173
+ writable: false,
174
+ value: { value: "" }
175
+ });
176
+ onChange(event);
177
+ } else if (inputRef.current) inputRef.current.value = "";
178
+ onClear?.();
179
+ };
180
+ /**
181
+ * 兼容 受控 和 非受控 两种状态
182
+ * 受控模式下,value 和 onChange 必须同时存在且正确传递。
183
+ * 非受控模式下,不要传 value,可以传 defaultValue。
184
+ */
185
+ return /* @__PURE__ */ jsxs("div", {
186
+ style: {
187
+ position: "relative",
188
+ display: "inline-block"
189
+ },
190
+ children: [/* @__PURE__ */ jsx("input", {
191
+ ref: inputRef,
192
+ name,
193
+ placeholder,
194
+ value,
195
+ onChange,
196
+ disabled,
197
+ className: `input-${theme}`,
198
+ style: { paddingRight: clearable ? "20px" : void 0 },
199
+ ...rest
200
+ }), clearable && (value || inputRef.current?.value) && /* @__PURE__ */ jsx("span", {
201
+ role: "button",
202
+ "aria-label": "clear",
203
+ onClick: handleClear,
204
+ style: {
205
+ position: "absolute",
206
+ right: "5px",
207
+ top: "50%",
208
+ transform: "translateY(-50%)",
209
+ cursor: "pointer"
210
+ },
211
+ children: "✕"
212
+ })]
213
+ });
214
+ }));
215
+
216
+ //#endregion
217
+ //#region src/SearchInput/style.ts
218
+ const Container$1 = styled.div`position:relative; width:100%;`;
219
+ const Input$1 = styled.input`
220
+ width:100%; padding:8px 12px; border:1px solid #d9d9d9; border-radius:4px; font-size:14px;
221
+ &:focus { outline:none; border-color:#1890ff; box-shadow:0 0 0 2px rgba(24,144,255,0.2); }
222
+ &:disabled { background:#f5f5f5; cursor:not-allowed; }
223
+ `;
224
+ const SuggestionsList = styled.ul`
225
+ position:absolute; top:100%; left:0; right:0; margin:4px 0 0; padding:4px 0;
226
+ list-style:none; background:white; border:1px solid #d9d9d9; border-radius:4px;
227
+ box-shadow:0 2px 8px rgba(0,0,0,0.15); z-index:1000; max-height:200px; overflow-y:auto;
228
+ `;
229
+ const SuggestionItem = styled.li`
230
+ padding:8px 12px; cursor:pointer; transition:background 0.2s;
231
+ &:hover { background:#f5f5f5; }
232
+ &.selected { background:#e6f7ff; }
233
+ `;
234
+
235
+ //#endregion
236
+ //#region src/SearchInput/index.tsx
237
+ const SearchInput = ({ debounceTime: debounceMs = 300, minSearchLength = 2, onSearch, searchResults$, showSuggestions = true, placeholder = "搜索...", ...rest }) => {
238
+ const [value, setValue] = useState("");
239
+ const [suggestions, setSuggestions] = useState([]);
240
+ const [showList, setShowList] = useState(false);
241
+ const [selectedIndex, setSelectedIndex] = useState(-1);
242
+ const inputRef = useRef(null);
243
+ const containerRef = useRef(null);
244
+ const input$ = useMemo(() => {
245
+ if (!inputRef.current) return null;
246
+ return fromEvent(inputRef.current, "input").pipe(map((e) => e.target.value), filter((v) => v.length >= minSearchLength || v.length === 0), debounceTime(debounceMs), distinctUntilChanged());
247
+ }, [debounceMs, minSearchLength]);
248
+ useEffect(() => {
249
+ if (!input$) return;
250
+ const subscription = input$.subscribe((value) => {
251
+ setValue(value);
252
+ onSearch?.(value);
253
+ });
254
+ return () => subscription.unsubscribe();
255
+ }, [input$, onSearch]);
256
+ useEffect(() => {
257
+ if (!searchResults$) {
258
+ setSuggestions([]);
259
+ return;
260
+ }
261
+ const subscription = searchResults$.subscribe((results) => {
262
+ setSuggestions(results);
263
+ if (results.length > 0) setShowList(true);
264
+ });
265
+ return () => subscription.unsubscribe();
266
+ }, [searchResults$]);
267
+ useEffect(() => {
268
+ if (searchResults$) return;
269
+ if (value.length >= minSearchLength) setSuggestions([`结果1: ${value}`, `结果2: ${value}`]);
270
+ else setSuggestions([]);
271
+ }, [
272
+ value,
273
+ minSearchLength,
274
+ searchResults$
275
+ ]);
276
+ useEffect(() => {
277
+ const handleClickOutside = (event) => {
278
+ if (containerRef.current && !containerRef.current.contains(event.target)) setShowList(false);
279
+ };
280
+ document.addEventListener("mousedown", handleClickOutside);
281
+ return () => document.removeEventListener("mousedown", handleClickOutside);
282
+ }, []);
283
+ const handleKeyDown = (e) => {
284
+ switch (e.key) {
285
+ case "ArrowDown":
286
+ e.preventDefault();
287
+ setSelectedIndex((i) => Math.min(i + 1, suggestions.length - 1));
288
+ break;
289
+ case "ArrowUp":
290
+ e.preventDefault();
291
+ setSelectedIndex((i) => Math.max(i - 1, -1));
292
+ break;
293
+ case "Enter":
294
+ if (selectedIndex >= 0) {
295
+ const selected = suggestions[selectedIndex];
296
+ setValue(selected);
297
+ setShowList(false);
298
+ onSearch?.(selected);
299
+ }
300
+ break;
301
+ case "Escape":
302
+ setShowList(false);
303
+ break;
304
+ }
305
+ };
306
+ return /* @__PURE__ */ jsxs(Container$1, {
307
+ ref: containerRef,
308
+ children: [/* @__PURE__ */ jsx(Input$1, {
309
+ ref: inputRef,
310
+ value,
311
+ onChange: (e) => setValue(e.target.value),
312
+ onFocus: () => suggestions.length > 0 && setShowList(true),
313
+ onKeyDown: handleKeyDown,
314
+ placeholder,
315
+ ...rest
316
+ }), showSuggestions && showList && suggestions.length > 0 && /* @__PURE__ */ jsx(SuggestionsList, { children: suggestions.map((s, i) => /* @__PURE__ */ jsx(SuggestionItem, {
317
+ className: i === selectedIndex ? "selected" : "",
318
+ onClick: () => {
319
+ setValue(s);
320
+ setShowList(false);
321
+ onSearch?.(s);
322
+ },
323
+ children: s
324
+ }, i)) })]
325
+ });
326
+ };
327
+
328
+ //#endregion
329
+ //#region src/Card/style.ts
330
+ const CardContainer = styled.div`
331
+ background: ${({ theme }) => theme.colors.background};
332
+ border-radius: ${({ theme }) => theme.borderRadius.md}px;
333
+ transition: all ${({ theme }) => theme.transitions.normal};
334
+ overflow: hidden;
335
+
336
+ /* 边框 */
337
+ ${({ bordered, theme }) => bordered && css`
338
+ border: 1px solid ${theme.colors.border};
339
+ `}
340
+
341
+ /* 阴影 */
342
+ ${({ shadow, theme }) => {
343
+ if (shadow === "always") return css`box-shadow: ${theme.shadows.md};`;
344
+ if (shadow === "hover") return css`
345
+ &:hover {
346
+ box-shadow: ${theme.shadows.md};
347
+ }
348
+ `;
349
+ if (shadow === true) return css`box-shadow: ${theme.shadows.sm};`;
350
+ return "";
351
+ }}
352
+
353
+ /* 悬停效果 */
354
+ ${({ hoverable, theme }) => hoverable && css`
355
+ cursor: pointer;
356
+ &:hover {
357
+ transform: translateY(-2px);
358
+ box-shadow: ${theme.shadows.lg};
359
+ }
360
+ `}
361
+
362
+ /* 尺寸 */
363
+ ${({ size, theme }) => {
364
+ return css`
365
+ padding: ${{
366
+ small: theme.spacing.sm,
367
+ default: theme.spacing.md,
368
+ large: theme.spacing.lg
369
+ }[size || "default"]}px;
370
+ `;
371
+ }}
372
+ `;
373
+ const CardHeader = styled.div`
374
+ display: flex;
375
+ align-items: center;
376
+ justify-content: space-between;
377
+ margin-bottom: ${({ theme }) => theme.spacing.sm}px;
378
+
379
+ h3 {
380
+ margin: 0;
381
+ font-size: ${({ theme }) => theme.typography.fontSize.md}px;
382
+ font-weight: ${({ theme }) => theme.typography.fontWeight.medium};
383
+ color: ${({ theme }) => theme.colors.text.primary};
384
+ }
385
+ `;
386
+ const CardCover = styled.div`
387
+ margin: -${({ theme }) => theme.spacing.md}px -${({ theme }) => theme.spacing.md}px
388
+ ${({ theme }) => theme.spacing.md}px -${({ theme }) => theme.spacing.md}px;
389
+
390
+ img {
391
+ width: 100%;
392
+ height: auto;
393
+ display: block;
394
+ }
395
+ `;
396
+ const CardContent = styled.div`
397
+ color: ${({ theme }) => theme.colors.text.secondary};
398
+ font-size: ${({ theme }) => theme.typography.fontSize.sm}px;
399
+ line-height: 1.5;
400
+ `;
401
+ const CardActions = styled.div`
402
+ display: flex;
403
+ align-items: center;
404
+ justify-content: space-around;
405
+ margin-top: ${({ theme }) => theme.spacing.md}px;
406
+ padding-top: ${({ theme }) => theme.spacing.sm}px;
407
+ border-top: 1px solid ${({ theme }) => theme.colors.border};
408
+ `;
409
+ const CardAction = styled.div`
410
+ flex: 1;
411
+ display: flex;
412
+ align-items: center;
413
+ justify-content: center;
414
+ cursor: pointer;
415
+ padding: ${({ theme }) => theme.spacing.xs}px;
416
+ transition: color ${({ theme }) => theme.transitions.fast};
417
+
418
+ &:hover {
419
+ color: ${({ theme }) => theme.colors.primary};
420
+ }
421
+ `;
422
+ const CardLoading = styled.div`
423
+ .loading-line {
424
+ height: 16px;
425
+ background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
426
+ background-size: 200% 100%;
427
+ animation: loading 1.5s infinite;
428
+ border-radius: ${({ theme }) => theme.borderRadius.sm}px;
429
+ margin-bottom: ${({ theme }) => theme.spacing.xs}px;
430
+
431
+ &:last-child {
432
+ width: 60%;
433
+ }
434
+ }
435
+
436
+ @keyframes loading {
437
+ 0% { background-position: 200% 0; }
438
+ 100% { background-position: -200% 0; }
439
+ }
440
+ `;
441
+
442
+ //#endregion
443
+ //#region src/Card/index.tsx
444
+ const Card = ({ title, extra, children, bordered = true, shadow = false, size = "default", hoverable = false, cover, actions, loading = false, onClick, className }) => {
445
+ const renderLoading = () => /* @__PURE__ */ jsxs(CardLoading, { children: [
446
+ /* @__PURE__ */ jsx("div", {
447
+ className: "loading-line",
448
+ style: { width: "40%" }
449
+ }),
450
+ /* @__PURE__ */ jsx("div", { className: "loading-line" }),
451
+ /* @__PURE__ */ jsx("div", { className: "loading-line" }),
452
+ /* @__PURE__ */ jsx("div", { className: "loading-line" })
453
+ ] });
454
+ const renderActions = () => {
455
+ if (!actions?.length) return null;
456
+ return /* @__PURE__ */ jsx(CardActions, { children: actions.map((action, index) => /* @__PURE__ */ jsx(CardAction, { children: action }, index)) });
457
+ };
458
+ return /* @__PURE__ */ jsxs(CardContainer, {
459
+ bordered,
460
+ shadow,
461
+ size,
462
+ hoverable,
463
+ onClick,
464
+ className,
465
+ children: [
466
+ cover && /* @__PURE__ */ jsx(CardCover, { children: cover }),
467
+ (title || extra) && /* @__PURE__ */ jsxs(CardHeader, { children: [/* @__PURE__ */ jsx("h3", { children: title }), /* @__PURE__ */ jsx("div", { children: extra })] }),
468
+ /* @__PURE__ */ jsx(CardContent, { children: loading ? renderLoading() : children }),
469
+ renderActions()
470
+ ]
471
+ });
472
+ };
473
+
474
+ //#endregion
475
+ //#region src/VirtualList/index.tsx
476
+ const Container = styled.div`
477
+ position: relative;
478
+ overflow-y: auto;
479
+ height: 100%;
480
+ `;
481
+ const Content = styled.div`
482
+ position: relative;
483
+ width: 100%;
484
+ `;
485
+ const Item = styled.div`
486
+ position: absolute;
487
+ top: ${({ top }) => top}px;
488
+ left: 0;
489
+ right: 0;
490
+ height: ${({ height }) => height}px;
491
+ `;
492
+ function VirtualList({ data, renderItem, itemHeight, height, overscan = 3, keyExtractor = (_, index) => index.toString() }) {
493
+ const [scrollTop, setScrollTop] = useState(0);
494
+ const containerRef = useRef(null);
495
+ useEffect(() => {
496
+ if (!containerRef.current) return;
497
+ const subscription = fromEvent(containerRef.current, "scroll").pipe(throttleTime(16)).subscribe(() => {
498
+ if (containerRef.current) setScrollTop(containerRef.current.scrollTop);
499
+ });
500
+ return () => subscription.unsubscribe();
501
+ }, []);
502
+ const totalHeight = data.length * itemHeight;
503
+ const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
504
+ const endIndex = Math.min(data.length, Math.ceil((scrollTop + height) / itemHeight) + overscan);
505
+ const visibleData = data.slice(startIndex, endIndex);
506
+ useCallback((index) => ({
507
+ top: index * itemHeight,
508
+ height: itemHeight
509
+ }), [itemHeight]);
510
+ return /* @__PURE__ */ jsx(Container, {
511
+ ref: containerRef,
512
+ style: { height },
513
+ children: /* @__PURE__ */ jsx(Content, {
514
+ style: { height: totalHeight },
515
+ children: visibleData.map((item, index) => {
516
+ const actualIndex = startIndex + index;
517
+ return /* @__PURE__ */ jsx(Item, {
518
+ top: actualIndex * itemHeight,
519
+ height: itemHeight,
520
+ children: renderItem(item, actualIndex)
521
+ }, keyExtractor(item, actualIndex));
522
+ })
523
+ })
524
+ });
525
+ }
526
+
527
+ //#endregion
528
+ //#region src/hooks/useDebounce.ts
529
+ /**
530
+ * 对传入的值进行防抖处理
531
+ * @param value 需要防抖的值
532
+ * @param delay 延迟时间(毫秒),默认 300ms
533
+ * @returns 防抖后的值
534
+ */
535
+ function useDebounce(value, delay = 300) {
536
+ const [debouncedValue, setDebouncedValue] = useState(value);
537
+ useEffect(() => {
538
+ const subject = new Subject();
539
+ const subscription = subject.pipe(debounceTime(delay), distinctUntilChanged()).subscribe(setDebouncedValue);
540
+ subject.next(value);
541
+ return () => {
542
+ subject.complete();
543
+ subscription.unsubscribe();
544
+ };
545
+ }, [value, delay]);
546
+ return debouncedValue;
547
+ }
548
+
549
+ //#endregion
550
+ //#region src/hooks/useObservable.ts
551
+ function useObservable(observable, initialValue) {
552
+ const [value, setValue] = useState(initialValue);
553
+ useEffect(() => {
554
+ const subscription = observable.subscribe(setValue);
555
+ return () => subscription.unsubscribe();
556
+ }, [observable]);
557
+ return value;
558
+ }
559
+
560
+ //#endregion
561
+ //#region ../theme/src/tokens.ts
562
+ const defaultTheme = {
563
+ colors: {
564
+ primary: "#1890ff",
565
+ secondary: "#f0f2f5",
566
+ success: "#52c41a",
567
+ warning: "#faad14",
568
+ error: "#f5222d",
569
+ info: "#1890ff",
570
+ background: "#ffffff",
571
+ text: {
572
+ primary: "#333333",
573
+ secondary: "#666666",
574
+ disabled: "#999999"
575
+ },
576
+ border: "#d9d9d9"
577
+ },
578
+ spacing: {
579
+ xs: 4,
580
+ sm: 8,
581
+ md: 16,
582
+ lg: 24,
583
+ xl: 32
584
+ },
585
+ typography: {
586
+ fontSize: {
587
+ xs: 12,
588
+ sm: 14,
589
+ md: 16,
590
+ lg: 18,
591
+ xl: 20
592
+ },
593
+ fontWeight: {
594
+ normal: 400,
595
+ medium: 500,
596
+ bold: 600
597
+ }
598
+ },
599
+ borderRadius: {
600
+ xs: 2,
601
+ sm: 4,
602
+ md: 6,
603
+ lg: 8,
604
+ xl: 12,
605
+ round: "50%"
606
+ },
607
+ shadows: {
608
+ sm: "0 2px 4px rgba(0, 0, 0, 0.1)",
609
+ md: "0 4px 8px rgba(0, 0, 0, 0.15)",
610
+ lg: "0 8px 16px rgba(0, 0, 0, 0.2)"
611
+ },
612
+ transitions: {
613
+ fast: "0.1s ease",
614
+ normal: "0.2s ease",
615
+ slow: "0.3s ease"
616
+ }
617
+ };
618
+
619
+ //#endregion
620
+ //#region ../theme/src/index.tsx
621
+ function deepMerge(target, source) {
622
+ const output = { ...target };
623
+ if (isObject(target) && isObject(source)) Object.keys(source).forEach((key) => {
624
+ const k = key;
625
+ if (isObject(source[k])) if (!(k in target)) output[k] = source[k];
626
+ else output[k] = deepMerge(target[k], source[k]);
627
+ else output[k] = source[k];
628
+ });
629
+ return output;
630
+ }
631
+ function isObject(item) {
632
+ return item && typeof item === "object" && !Array.isArray(item);
633
+ }
634
+ const ThemeContext = createContext(defaultTheme);
635
+ const useTheme = () => {
636
+ const theme = useContext(ThemeContext);
637
+ if (!theme) throw new Error("useTheme must be used within a ThemeProvider");
638
+ return theme;
639
+ };
640
+ const ThemeProvider = ({ theme = {}, children }) => {
641
+ const mergedTheme = useMemo(() => deepMerge(defaultTheme, theme), [theme]);
642
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, {
643
+ value: mergedTheme,
644
+ children: /* @__PURE__ */ jsx(ThemeProvider$1, {
645
+ theme: mergedTheme,
646
+ children
647
+ })
648
+ });
649
+ };
650
+
651
+ //#endregion
652
+ export { Button, Card, Input, SearchInput, ThemeProvider, VirtualList, useDebounce, useObservable, useTheme };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@react-component-library-nlxx/components",
3
+ "version": "1.0.1",
4
+ "description": "A React component library",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build:components": "tsdown"
13
+ },
14
+ "peerDependencies": {
15
+ "react": ">=18",
16
+ "react-dom": ">=18",
17
+ "rxjs": ">=7",
18
+ "styled-components": ">=6"
19
+ },
20
+ "private": false,
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://gitee.com/li-jun-xun/react-component-library.git"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "keywords": [
30
+ "react",
31
+ "components",
32
+ "ui"
33
+ ],
34
+ "dependencies": {
35
+ "@react-component-library/theme": "workspace:*",
36
+ "react-hook-form": "^7.71.2"
37
+ },
38
+ "devDependencies": {
39
+ "tsdown": "^0.20.3"
40
+ }
41
+ }