@aozi6666/bee-design 0.1.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.
Files changed (76) hide show
  1. package/README.md +73 -0
  2. package/build/App.d.ts +4 -0
  3. package/build/App.js +137 -0
  4. package/build/components/AutoComplete/autoComplete.d.ts +13 -0
  5. package/build/components/AutoComplete/autoComplete.js +123 -0
  6. package/build/components/AutoComplete/autoComplete.types.d.ts +19 -0
  7. package/build/components/AutoComplete/autoComplete.types.js +1 -0
  8. package/build/components/AutoComplete/autoCompleteDropdown.d.ts +13 -0
  9. package/build/components/AutoComplete/autoCompleteDropdown.js +17 -0
  10. package/build/components/AutoComplete/index.d.ts +4 -0
  11. package/build/components/AutoComplete/index.js +3 -0
  12. package/build/components/Button/button.d.ts +6 -0
  13. package/build/components/Button/button.js +43 -0
  14. package/build/components/Button/button.types.d.ts +24 -0
  15. package/build/components/Button/button.types.js +19 -0
  16. package/build/components/Button/index.d.ts +2 -0
  17. package/build/components/Button/index.js +2 -0
  18. package/build/components/Icon/icon.d.ts +16 -0
  19. package/build/components/Icon/icon.js +24 -0
  20. package/build/components/Icon/icon.types.d.ts +6 -0
  21. package/build/components/Icon/icon.types.js +2 -0
  22. package/build/components/Icon/index.d.ts +2 -0
  23. package/build/components/Icon/index.js +2 -0
  24. package/build/components/Input/index.d.ts +4 -0
  25. package/build/components/Input/index.js +3 -0
  26. package/build/components/Input/input.d.ts +5 -0
  27. package/build/components/Input/input.js +32 -0
  28. package/build/components/Input/input.types.d.ts +16 -0
  29. package/build/components/Input/input.types.js +1 -0
  30. package/build/components/Menu/index.d.ts +10 -0
  31. package/build/components/Menu/index.js +9 -0
  32. package/build/components/Menu/menu.d.ts +34 -0
  33. package/build/components/Menu/menu.js +48 -0
  34. package/build/components/Menu/menuItem.d.ts +14 -0
  35. package/build/components/Menu/menuItem.js +20 -0
  36. package/build/components/Menu/subMenu.d.ts +11 -0
  37. package/build/components/Menu/subMenu.js +56 -0
  38. package/build/components/Progress/index.d.ts +2 -0
  39. package/build/components/Progress/index.js +2 -0
  40. package/build/components/Progress/progress.d.ts +4 -0
  41. package/build/components/Progress/progress.js +6 -0
  42. package/build/components/Progress/progress.types.d.ts +9 -0
  43. package/build/components/Progress/progress.types.js +2 -0
  44. package/build/components/Transition/index.d.ts +3 -0
  45. package/build/components/Transition/index.js +2 -0
  46. package/build/components/Transition/transition.d.ts +4 -0
  47. package/build/components/Transition/transition.js +18 -0
  48. package/build/components/Transition/transition.types.d.ts +10 -0
  49. package/build/components/Transition/transition.types.js +1 -0
  50. package/build/components/Upload/dragger.d.ts +7 -0
  51. package/build/components/Upload/dragger.js +42 -0
  52. package/build/components/Upload/index.d.ts +2 -0
  53. package/build/components/Upload/index.js +2 -0
  54. package/build/components/Upload/native/axios-react.d.ts +2 -0
  55. package/build/components/Upload/native/axios-react.js +99 -0
  56. package/build/components/Upload/native/from-html.d.ts +2 -0
  57. package/build/components/Upload/native/from-html.js +5 -0
  58. package/build/components/Upload/upload.d.ts +13 -0
  59. package/build/components/Upload/upload.js +192 -0
  60. package/build/components/Upload/upload.types.d.ts +48 -0
  61. package/build/components/Upload/upload.types.js +3 -0
  62. package/build/components/Upload/uploadList.d.ts +8 -0
  63. package/build/components/Upload/uploadList.js +13 -0
  64. package/build/hooks/useClickOutside.d.ts +3 -0
  65. package/build/hooks/useClickOutside.js +18 -0
  66. package/build/hooks/useDebounce.d.ts +2 -0
  67. package/build/hooks/useDebounce.js +14 -0
  68. package/build/index.css +856 -0
  69. package/build/index.css.map +1 -0
  70. package/build/index.d.ts +9 -0
  71. package/build/index.js +12 -0
  72. package/build/main.d.ts +1 -0
  73. package/build/main.js +7 -0
  74. package/build/setupTests.d.ts +1 -0
  75. package/build/setupTests.js +1 -0
  76. package/package.json +109 -0
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # React + TypeScript + Vite
2
+
3
+ This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4
+
5
+ Currently, two official plugins are available:
6
+
7
+ - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
8
+ - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9
+
10
+ ## React Compiler
11
+
12
+ The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
13
+
14
+ ## Expanding the ESLint configuration
15
+
16
+ If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
17
+
18
+ ```js
19
+ export default defineConfig([
20
+ globalIgnores(['dist']),
21
+ {
22
+ files: ['**/*.{ts,tsx}'],
23
+ extends: [
24
+ // Other configs...
25
+
26
+ // Remove tseslint.configs.recommended and replace with this
27
+ tseslint.configs.recommendedTypeChecked,
28
+ // Alternatively, use this for stricter rules
29
+ tseslint.configs.strictTypeChecked,
30
+ // Optionally, add this for stylistic rules
31
+ tseslint.configs.stylisticTypeChecked,
32
+
33
+ // Other configs...
34
+ ],
35
+ languageOptions: {
36
+ parserOptions: {
37
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
38
+ tsconfigRootDir: import.meta.dirname,
39
+ },
40
+ // other options...
41
+ },
42
+ },
43
+ ])
44
+ ```
45
+
46
+ You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
47
+
48
+ ```js
49
+ // eslint.config.js
50
+ import reactX from 'eslint-plugin-react-x'
51
+ import reactDom from 'eslint-plugin-react-dom'
52
+
53
+ export default defineConfig([
54
+ globalIgnores(['dist']),
55
+ {
56
+ files: ['**/*.{ts,tsx}'],
57
+ extends: [
58
+ // Other configs...
59
+ // Enable lint rules for React
60
+ reactX.configs['recommended-typescript'],
61
+ // Enable lint rules for React DOM
62
+ reactDom.configs.recommended,
63
+ ],
64
+ languageOptions: {
65
+ parserOptions: {
66
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
67
+ tsconfigRootDir: import.meta.dirname,
68
+ },
69
+ // other options...
70
+ },
71
+ },
72
+ ])
73
+ ```
package/build/App.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { FC } from 'react';
2
+ import './styles/index.scss';
3
+ declare const App: FC;
4
+ export default App;
package/build/App.js ADDED
@@ -0,0 +1,137 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import './styles/index.scss';
3
+ import Button from './components/Button/button';
4
+ import { ButtonType } from './components/Button/button.types';
5
+ import Upload from './components/Upload';
6
+ import Icon from './components/Icon';
7
+ import Progress from './components/Progress';
8
+ import AutoComplete from './components/AutoComplete';
9
+ const App = () => {
10
+ const defaultUploadList = [
11
+ {
12
+ uid: '1',
13
+ size: 1024,
14
+ name: '已上传文件.png',
15
+ status: 'success',
16
+ percent: 100,
17
+ },
18
+ {
19
+ uid: '2',
20
+ size: 2048,
21
+ name: '上传中文件.jpg',
22
+ status: 'uploading',
23
+ percent: 40,
24
+ },
25
+ ];
26
+ const lakersWithNumber = [
27
+ { value: 'bradley', number: 11 },
28
+ { value: 'pope', number: 1 },
29
+ { value: 'caruso', number: 4 },
30
+ { value: 'cook', number: 2 },
31
+ { value: 'cousins', number: 15 },
32
+ { value: 'james', number: 23 },
33
+ { value: 'AD', number: 3 },
34
+ { value: 'green', number: 14 },
35
+ { value: 'howard', number: 39 },
36
+ { value: 'kuzma', number: 0 },
37
+ ];
38
+ const handleAutoCompleteFetch = (query) => {
39
+ return lakersWithNumber.filter((player) => player.value.toLowerCase().includes(query.toLowerCase()));
40
+ };
41
+ return (_jsx("div", { style: {
42
+ minHeight: '100vh',
43
+ padding: '40px 24px 80px',
44
+ background: 'radial-gradient(circle at top left, #f0f5ff 0, transparent 50%), radial-gradient(circle at bottom right, #fff1f0 0, transparent 55%)',
45
+ fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif',
46
+ }, children: _jsxs("div", { style: {
47
+ maxWidth: 960,
48
+ margin: '0 auto',
49
+ }, children: [_jsxs("header", { style: {
50
+ marginBottom: 32,
51
+ display: 'flex',
52
+ alignItems: 'center',
53
+ justifyContent: 'space-between',
54
+ gap: 16,
55
+ }, children: [_jsxs("div", { children: [_jsx("h1", { style: { margin: 0, fontSize: 28 }, children: "Cream Design \u7EC4\u4EF6\u5E93" }), _jsx("p", { style: { margin: '8px 0 0', color: '#595959', fontSize: 14 }, children: "\u4E00\u5957\u7528\u6765\u7EC3\u4E60 React + TypeScript \u7684\u8F7B\u91CF\u7EC4\u4EF6\u793A\u4F8B\u3002" })] }), _jsxs("div", { style: { display: 'flex', gap: 12 }, children: [_jsx(Button, { btnType: ButtonType.Primary, children: "\u67E5\u770B\u6587\u6863" }), _jsx(Button, { btnType: ButtonType.Default, children: "Git \u4ED3\u5E93" })] })] }), _jsxs("main", { style: {
56
+ display: 'grid',
57
+ gridTemplateColumns: 'minmax(0, 1.2fr) minmax(0, 1fr)',
58
+ gap: 24,
59
+ alignItems: 'flex-start',
60
+ }, children: [_jsxs("section", { style: {
61
+ display: 'flex',
62
+ flexDirection: 'column',
63
+ gap: 24,
64
+ }, children: [_jsxs("div", { style: {
65
+ padding: 24,
66
+ borderRadius: 12,
67
+ background: '#ffffff',
68
+ boxShadow: '0 8px 24px rgba(15, 23, 42, 0.06)',
69
+ }, children: [_jsx("h2", { style: { margin: 0, marginBottom: 16, fontSize: 18 }, children: "Button \u7EC4\u4EF6\u6F14\u793A" }), _jsx("p", { style: { margin: '0 0 16px', color: '#8c8c8c', fontSize: 13 }, children: "\u4E0D\u540C\u7C7B\u578B\u6309\u94AE\u548C\u94FE\u63A5\u6309\u94AE\uFF0C\u9002\u5408\u4F5C\u4E3A\u9875\u9762\u4E3B\u64CD\u4F5C\u3001\u6B21\u8981\u64CD\u4F5C\u548C\u6587\u672C\u94FE\u63A5\u3002" }), _jsxs("div", { style: {
70
+ display: 'flex',
71
+ flexWrap: 'wrap',
72
+ gap: 12,
73
+ }, children: [_jsx(Button, { btnType: ButtonType.Primary, children: "Primary Button" }), _jsx(Button, { btnType: ButtonType.Default, children: "Default Button" }), _jsx(Button, { btnType: ButtonType.Danger, children: "Danger Button" }), _jsx(Button, { btnType: ButtonType.Link, href: "https://www.baidu.com/", children: "Link \u767E\u5EA6" }), _jsx(Button, { btnType: ButtonType.Link, href: "https://www.baidu.com/", target: "_blank", children: "Link \u767E\u5EA6(\"\u65B0\u7A97\u53E3\u6253\u5F00\")" })] })] }), _jsxs("div", { style: {
74
+ padding: 24,
75
+ borderRadius: 12,
76
+ background: '#ffffff',
77
+ boxShadow: '0 8px 24px rgba(15, 23, 42, 0.06)',
78
+ }, children: [_jsx("h2", { style: { margin: 0, marginBottom: 16, fontSize: 18 }, children: "Upload \u7EC4\u4EF6\u6F14\u793A" }), _jsx("p", { style: { margin: '0 0 16px', color: '#8c8c8c', fontSize: 13 }, children: "\u652F\u6301\u70B9\u51FB\u9009\u62E9\u3001\u62D6\u62FD\u4E0A\u4F20\u3001\u4E0A\u4F20\u8FDB\u5EA6\u663E\u793A\u548C\u6587\u4EF6\u5217\u8868\u7BA1\u7406\u3002" }), _jsxs("div", { style: {
79
+ display: 'flex',
80
+ flexDirection: 'column',
81
+ gap: 16,
82
+ alignItems: 'stretch',
83
+ }, children: [_jsx(Upload, { action: "https://jsonplaceholder.typicode.com/posts", defaultFileList: defaultUploadList, children: _jsx(Button, { btnType: ButtonType.Primary, children: "\u70B9\u51FB\u4E0A\u4F20" }) }), _jsx(Upload, { action: "https://jsonplaceholder.typicode.com/posts", drag: true, children: _jsx("div", { style: {
84
+ padding: '20px 40px',
85
+ border: '1px dashed #d9d9d9',
86
+ borderRadius: 4,
87
+ textAlign: 'center',
88
+ color: '#595959',
89
+ background: '#fafafa',
90
+ }, children: "\u62D6\u62FD\u6587\u4EF6\u5230\u6B64\u5904\uFF0C\u6216\u70B9\u51FB\u4E0A\u4F20" }) })] })] })] }), _jsxs("section", { style: {
91
+ display: 'flex',
92
+ flexDirection: 'column',
93
+ gap: 24,
94
+ }, children: [_jsxs("div", { style: {
95
+ padding: 24,
96
+ borderRadius: 12,
97
+ background: '#ffffff',
98
+ boxShadow: '0 8px 24px rgba(15, 23, 42, 0.06)',
99
+ }, children: [_jsx("h2", { style: { margin: 0, marginBottom: 16, fontSize: 18 }, children: "Icon \u7EC4\u4EF6\u6F14\u793A" }), _jsx("p", { style: { margin: '0 0 16px', color: '#8c8c8c', fontSize: 13 }, children: "\u57FA\u4E8E Font Awesome \u7684\u56FE\u6807\u7EC4\u4EF6\uFF0C\u652F\u6301\u4E3B\u9898\u8272\u548C\u6240\u6709\u539F\u751F\u5C5E\u6027\u3002" }), _jsxs("div", { style: {
100
+ display: 'flex',
101
+ flexWrap: 'wrap',
102
+ gap: 20,
103
+ alignItems: 'center',
104
+ }, children: [_jsx(Icon, { icon: "coffee", size: "2x" }), _jsx(Icon, { icon: "check-circle", size: "2x", theme: "success" }), _jsx(Icon, { icon: "times", size: "2x", theme: "danger" }), _jsx(Icon, { icon: "spinner", size: "2x", spin: true, theme: "primary" })] })] }), _jsxs("div", { style: {
105
+ padding: 24,
106
+ borderRadius: 12,
107
+ background: '#ffffff',
108
+ boxShadow: '0 8px 24px rgba(15, 23, 42, 0.06)',
109
+ }, children: [_jsx("h2", { style: { margin: 0, marginBottom: 16, fontSize: 18 }, children: "Progress \u7EC4\u4EF6\u6F14\u793A" }), _jsx("p", { style: { margin: '0 0 16px', color: '#8c8c8c', fontSize: 13 }, children: "\u5C55\u793A\u4EFB\u52A1\u5B8C\u6210\u8FDB\u5EA6\uFF0C\u652F\u6301\u4E0D\u540C\u4E3B\u9898\u989C\u8272\u548C\u9AD8\u5EA6\u3002" }), _jsxs("div", { style: {
110
+ display: 'flex',
111
+ flexDirection: 'column',
112
+ gap: 12,
113
+ }, children: [_jsx(Progress, { percent: 30 }), _jsx(Progress, { percent: 65, theme: "success" }), _jsx(Progress, { percent: 90, theme: "danger", strokeHeight: 10 })] })] }), _jsxs("div", { style: {
114
+ padding: 24,
115
+ borderRadius: 12,
116
+ background: '#ffffff',
117
+ boxShadow: '0 8px 24px rgba(15, 23, 42, 0.06)',
118
+ }, children: [_jsx("h2", { style: { margin: 0, marginBottom: 16, fontSize: 18 }, children: "AutoComplete \u7EC4\u4EF6\u6F14\u793A" }), _jsx("p", { style: { margin: '0 0 16px', color: '#8c8c8c', fontSize: 13 }, children: "\u8F93\u5165\u5185\u5BB9\u81EA\u52A8\u7ED9\u51FA\u5019\u9009\u9879\uFF0C\u652F\u6301\u952E\u76D8\u9009\u62E9\u4E0E\u81EA\u5B9A\u4E49\u4E0B\u62C9\u6E32\u67D3\u3002" }), _jsx(AutoComplete, { fetchSuggestions: handleAutoCompleteFetch, placeholder: "\u8F93\u5165\u6E56\u4EBA\u961F\u7403\u5458\u82F1\u6587\u540D\u8BD5\u8BD5\uFF08\u5982\uFF1Aja / co\uFF09", renderOption: (item) => {
119
+ const player = item;
120
+ return (_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', gap: 12 }, children: [_jsx("b", { children: player.value }), _jsxs("span", { style: { color: '#8c8c8c' }, children: ["#", player.number] })] }));
121
+ } })] })] })] }), _jsxs("div", { style: {
122
+ marginTop: 40,
123
+ padding: 24,
124
+ borderRadius: 12,
125
+ background: '#ffffff',
126
+ boxShadow: '0 8px 24px rgba(15, 23, 42, 0.06)',
127
+ }, children: [_jsx("h2", { style: { margin: 0, marginBottom: 16, fontSize: 18 }, children: "\u539F\u751F\u8868\u5355\u4E0A\u4F20\u793A\u4F8B" }), _jsxs("p", { style: { margin: '0 0 16px', color: '#8c8c8c', fontSize: 13 }, children: ["\u4F7F\u7528\u539F\u751F ", _jsx("code", { children: "<form>" }), " \u63D0\u4EA4\u5230\u540E\u7AEF\u63A5\u53E3\u7684\u6587\u4EF6\u4E0A\u4F20\u65B9\u5F0F\u3002"] }), _jsxs("form", { method: "post", encType: "multipart/form-data", action: "https://jsonplaceholder.typicode.com/posts", style: { marginTop: 8 }, children: [_jsx("input", { type: "file", name: "myFile" }), _jsx("button", { type: "submit", style: {
128
+ marginLeft: 12,
129
+ padding: '4px 12px',
130
+ borderRadius: 4,
131
+ border: '1px solid #1677ff',
132
+ background: '#1677ff',
133
+ color: '#fff',
134
+ cursor: 'pointer',
135
+ }, children: "Submit" })] })] })] }) }));
136
+ };
137
+ export default App;
@@ -0,0 +1,13 @@
1
+ import type { AutoCompleteProps } from './autoComplete.types';
2
+ export type { AutoCompleteProps, DataSourceType } from './autoComplete.types';
3
+ /**
4
+ * 输入框自动完成功能。当输入值需要自动完成时使用,支持同步和异步两种方式
5
+ * 支持 Input 组件的所有属性 支持键盘事件选择
6
+ * ### 引用方法
7
+ *
8
+ * ~~~js
9
+ * import { AutoComplete } from 'vikingship'
10
+ * ~~~
11
+ */
12
+ export declare const AutoComplete: (props: AutoCompleteProps) => import("react/jsx-runtime").JSX.Element;
13
+ export default AutoComplete;
@@ -0,0 +1,123 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // AutoComplete 组件: 带搜索建议的 Input
3
+ import { useState, useEffect, useRef } from 'react';
4
+ import Input from '../Input/input';
5
+ import useDebounce from '../../hooks/useDebounce';
6
+ import useClickOutside from '../../hooks/useClickOutside';
7
+ import AutoCompleteDropdown from './autoCompleteDropdown';
8
+ /**
9
+ * 输入框自动完成功能。当输入值需要自动完成时使用,支持同步和异步两种方式
10
+ * 支持 Input 组件的所有属性 支持键盘事件选择
11
+ * ### 引用方法
12
+ *
13
+ * ~~~js
14
+ * import { AutoComplete } from 'vikingship'
15
+ * ~~~
16
+ */
17
+ export const AutoComplete = (props) => {
18
+ const { fetchSuggestions, onSelect, onChange, value, renderOption, ...restProps } = props;
19
+ // 输入框当前显示的文本
20
+ const [inputValue, setInputValue] = useState(value || '');
21
+ // 下拉建议列表的数据源(渲染 `<li>` 就靠它)
22
+ const [suggestions, setSugestions] = useState([]);
23
+ // 异步请求进行中就显示 loading
24
+ const [loading, setLoading] = useState(false);
25
+ // 是否展示下拉(配合 `Transition` 动画)
26
+ const [showDropdown, setShowDropdown] = useState(false);
27
+ // 键盘上下选择时,哪一项高亮(对应 class `is-active`)
28
+ const [highlightIndex, setHighlightIndex] = useState(-1);
29
+ // 两个关键 ref:
30
+ // 用来区分“用户打字触发搜索” vs “用户选中后把值塞回去(不应该再搜一次)”
31
+ const triggerSearch = useRef(false);
32
+ // 挂到最外层 div 上,给 `useClickOutside` 判断“点击是否发生在组件外”
33
+ const componentRef = useRef(null);
34
+ // 防抖Hook:把“频繁输入”变成“停顿后再触发一次”
35
+ const debouncedValue = useDebounce(inputValue, 300);
36
+ // 自定义Hook:点击组件外部时关闭下拉
37
+ /* 在 `document` 上挂一个 `click` 监听
38
+ * @param componentRef 组件的 ref
39
+ * @param callback 点击外部时执行的回调
40
+ */
41
+ useClickOutside(componentRef, () => {
42
+ setSugestions([]);
43
+ setShowDropdown(false);
44
+ });
45
+ // 监听 `debouncedValue` 变化
46
+ useEffect(() => {
47
+ if (debouncedValue && triggerSearch.current) {
48
+ setSugestions([]);
49
+ const results = fetchSuggestions(debouncedValue);
50
+ if (results instanceof Promise) {
51
+ setLoading(true);
52
+ results.then(data => {
53
+ setLoading(false);
54
+ setSugestions(data);
55
+ setShowDropdown(data.length > 0);
56
+ });
57
+ }
58
+ else {
59
+ setSugestions(results);
60
+ setShowDropdown(results.length > 0);
61
+ }
62
+ }
63
+ else {
64
+ setShowDropdown(false);
65
+ }
66
+ setHighlightIndex(-1);
67
+ }, [debouncedValue, fetchSuggestions]);
68
+ const highlight = (index) => {
69
+ if (index < 0)
70
+ index = 0;
71
+ if (index >= suggestions.length) {
72
+ index = suggestions.length - 1;
73
+ }
74
+ setHighlightIndex(index);
75
+ };
76
+ const handleKeyDown = (e) => {
77
+ switch (e.keyCode) {
78
+ case 13:
79
+ if (suggestions[highlightIndex]) {
80
+ handleSelect(suggestions[highlightIndex]);
81
+ }
82
+ break;
83
+ case 38:
84
+ highlight(highlightIndex - 1);
85
+ break;
86
+ case 40:
87
+ highlight(highlightIndex + 1);
88
+ break;
89
+ case 27:
90
+ setShowDropdown(false);
91
+ break;
92
+ default:
93
+ break;
94
+ }
95
+ };
96
+ // 回调:消息框内容发生变化
97
+ const handleChange = (e) => {
98
+ // 获取 输入框内容 (去掉首尾空格)
99
+ const value = e.target.value.trim();
100
+ // 更新 输入框内容
101
+ // `inputValue` 变化 → 经过 `useDebounce` 得到 `debouncedValue`
102
+ setInputValue(value);
103
+ // 组件使用者传来的回调:消息框内容发生变化
104
+ if (onChange) {
105
+ onChange(value);
106
+ }
107
+ // 告诉后面“这次是用户输入,应当触发搜索”
108
+ triggerSearch.current = true;
109
+ };
110
+ // 回调:用户选中某一项
111
+ const handleSelect = (item) => {
112
+ setInputValue(item.value);
113
+ setShowDropdown(false);
114
+ if (onSelect) {
115
+ onSelect(item);
116
+ }
117
+ triggerSearch.current = false;
118
+ };
119
+ return (_jsxs("div", { className: "viking-auto-complete", ref: componentRef, children: [_jsx(Input, { ...restProps, value: inputValue, onChange: handleChange, onKeyDown: handleKeyDown }), _jsx(AutoCompleteDropdown, { loading: loading, showDropdown: showDropdown, suggestions: suggestions, highlightIndex: highlightIndex, onSelect: handleSelect, renderOption: renderOption, onExited: () => {
120
+ setSugestions([]);
121
+ } })] }));
122
+ };
123
+ export default AutoComplete;
@@ -0,0 +1,19 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { InputProps } from '../Input/input.types';
3
+ interface DataSourceObject {
4
+ value: string;
5
+ }
6
+ export type DataSourceType<T = {}> = T & DataSourceObject;
7
+ export interface AutoCompleteProps extends Omit<InputProps, 'onSelect' | 'onChange'> {
8
+ /**
9
+ * 返回输入建议的方法,可以拿到当前的输入,然后返回同步的数组或者是异步的 Promise
10
+ */
11
+ fetchSuggestions: (str: string) => DataSourceType[] | Promise<DataSourceType[]>;
12
+ /** 点击 选中建议项(点中/回车) 时触发的回调 */
13
+ onSelect?: (item: DataSourceType) => void;
14
+ /** 文本框发生改变 的时候触发的事件 */
15
+ onChange?: (value: string) => void;
16
+ /** 支持自定义 渲染 下拉项 的 UI,返回 ReactElement类型 */
17
+ renderOption?: (item: DataSourceType) => ReactElement;
18
+ }
19
+ export {};
@@ -0,0 +1,13 @@
1
+ import type { FC, ReactElement } from 'react';
2
+ import type { DataSourceType } from './autoComplete.types';
3
+ export interface AutoCompleteDropdownProps {
4
+ loading: boolean;
5
+ showDropdown: boolean;
6
+ suggestions: DataSourceType[];
7
+ highlightIndex: number;
8
+ onSelect: (item: DataSourceType) => void;
9
+ renderOption?: (item: DataSourceType) => ReactElement;
10
+ onExited?: () => void;
11
+ }
12
+ export declare const AutoCompleteDropdown: FC<AutoCompleteDropdownProps>;
13
+ export default AutoCompleteDropdown;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import classNames from 'classnames';
3
+ import Icon from '../Icon/icon';
4
+ import Transition from '../Transition/transition';
5
+ export const AutoCompleteDropdown = (props) => {
6
+ const { loading, showDropdown, suggestions, highlightIndex, onSelect, renderOption, onExited, } = props;
7
+ const renderTemplate = (item) => {
8
+ return renderOption ? renderOption(item) : item.value;
9
+ };
10
+ return (_jsx(Transition, { in: showDropdown || loading, animation: "zoom-in-top", timeout: 300, onExited: onExited, children: _jsxs("ul", { className: "viking-suggestion-list", children: [loading && (_jsx("div", { className: "suggestions-loading-icon", children: _jsx(Icon, { icon: "spinner", spin: true }) })), suggestions.map((item, index) => {
11
+ const cnames = classNames('suggestion-item', {
12
+ 'is-active': index === highlightIndex,
13
+ });
14
+ return (_jsx("li", { className: cnames, onClick: () => onSelect(item), children: renderTemplate(item) }, index));
15
+ })] }) }));
16
+ };
17
+ export default AutoCompleteDropdown;
@@ -0,0 +1,4 @@
1
+ import AutoComplete from './autoComplete';
2
+ export default AutoComplete;
3
+ export * from './autoComplete';
4
+ export type * from './autoComplete.types';
@@ -0,0 +1,3 @@
1
+ import AutoComplete from './autoComplete';
2
+ export default AutoComplete;
3
+ export * from './autoComplete';
@@ -0,0 +1,6 @@
1
+ import type { FC } from 'react';
2
+ import { ButtonSize, ButtonType, type AnchorButtonProps, type ButtonProps, type NativeButtonProps } from './button.types';
3
+ declare const Button: FC<ButtonProps>;
4
+ export default Button;
5
+ export { ButtonSize, ButtonType };
6
+ export type { ButtonProps, AnchorButtonProps, NativeButtonProps };
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // 第三方工具库: 自动智能拼接 class 字符串
3
+ import classNames from 'classnames';
4
+ import { ButtonSize, ButtonType, } from './button.types';
5
+ const Button = ({ className, disabled = false, size, btnType = ButtonType.Default, children, href, ...restProps }) => {
6
+ // btn, btn-lg, btn-primary
7
+ // ① classNames 生成 class
8
+ /*
9
+ 用户写:<Button btnType="primary" size="lg">
10
+ 组件算出 class:btn btn-primary btn-lg
11
+ */
12
+ // 返回 字符串
13
+ const classes = classNames('btn', className, {
14
+ /*
15
+ 外部传来 <Button btnType={ButtonType.Primary}>
16
+ -> 变成 { "btn-primary": true }
17
+ -> classNames 会加上: btn-primary
18
+ -> 最终变为 : btn btn-primary
19
+ */
20
+ [`btn-${btnType}`]: btnType,
21
+ [`btn-${size}`]: size,
22
+ // 是 link 按钮 && disabled === true
23
+ // classNames 会加上: disabled
24
+ disabled: btnType === ButtonType.Link && disabled,
25
+ });
26
+ // ② 根据类型决定渲染:
27
+ /*
28
+ 用户写:<Button btnType="link" href="https://xxx">
29
+ 组件算出: <a class="btn btn-link">...</a>
30
+ */
31
+ if (btnType === ButtonType.Link && href) {
32
+ return (_jsx("a", { className: classes, href: href, ...restProps, children: children }));
33
+ }
34
+ /*
35
+ 用户写:<Button btnType="primary">
36
+ 组件算出: <button class="btn btn-primary">
37
+ */
38
+ return (_jsx("button", { className: classes, disabled: disabled, ...restProps, children: children }));
39
+ };
40
+ // 导出一个 React 组件函数
41
+ export default Button;
42
+ // 重新导出类型和枚举,方便测试等地方从 `./button` 统一导入
43
+ export { ButtonSize, ButtonType };
@@ -0,0 +1,24 @@
1
+ import type { AnchorHTMLAttributes, ButtonHTMLAttributes, ReactNode } from 'react';
2
+ export declare const ButtonSize: {
3
+ readonly Large: "lg";
4
+ readonly Small: "sm";
5
+ };
6
+ export type ButtonSize = (typeof ButtonSize)[keyof typeof ButtonSize];
7
+ export declare const ButtonType: {
8
+ readonly Primary: "primary";
9
+ readonly Default: "default";
10
+ readonly Danger: "danger";
11
+ readonly Link: "link";
12
+ };
13
+ export type ButtonType = (typeof ButtonType)[keyof typeof ButtonType];
14
+ export interface BaseButtonProps {
15
+ className?: string;
16
+ disabled?: boolean;
17
+ size?: ButtonSize;
18
+ btnType?: ButtonType;
19
+ children?: ReactNode;
20
+ href?: string;
21
+ }
22
+ export type NativeButtonProps = BaseButtonProps & ButtonHTMLAttributes<HTMLButtonElement>;
23
+ export type AnchorButtonProps = BaseButtonProps & AnchorHTMLAttributes<HTMLAnchorElement>;
24
+ export type ButtonProps = Partial<NativeButtonProps & AnchorButtonProps>;
@@ -0,0 +1,19 @@
1
+ /* 定义 Button 组件可以接收哪些 props(组件 API) */
2
+ // TS 导出 两个固定值 'lg' / 'sm'
3
+ /*
4
+ as const: 把 ButtonSize对象的值 变成不可修改的 字面量类型
5
+ - 如果没有 as const: Large: string
6
+ - 如果有 as const: Large: "lg"
7
+
8
+ 不写 as const: Large: 'lg' 会被 TS 自动推导为 string 类型
9
+ */
10
+ export const ButtonSize = {
11
+ Large: 'lg',
12
+ Small: 'sm',
13
+ };
14
+ export const ButtonType = {
15
+ Primary: 'primary',
16
+ Default: 'default',
17
+ Danger: 'danger',
18
+ Link: 'link',
19
+ };
@@ -0,0 +1,2 @@
1
+ import Button from './button';
2
+ export default Button;
@@ -0,0 +1,2 @@
1
+ import Button from './button';
2
+ export default Button;
@@ -0,0 +1,16 @@
1
+ import type { FC } from 'react';
2
+ import type { IconProps } from './icon.types';
3
+ /**
4
+ * 提供了一套常用的图标集合 基于 react-fontawesome。
5
+ *
6
+ * 支持 react-fontawesome的所有属性 可以在这里查询 https://github.com/FortAwesome/react-fontawesome#basic
7
+ *
8
+ * 支持 fontawesome 所有 free-solid-icons,可以在这里查看所有图标 https://fontawesome.com/icons?d=gallery&s=solid&m=free
9
+ * ### 引用方法
10
+ *
11
+ * ~~~js
12
+ * import { Icon } from 'vikingship'
13
+ * ~~~
14
+ */
15
+ export declare const Icon: FC<IconProps>;
16
+ export default Icon;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import classNames from 'classnames';
3
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
4
+ /**
5
+ * 提供了一套常用的图标集合 基于 react-fontawesome。
6
+ *
7
+ * 支持 react-fontawesome的所有属性 可以在这里查询 https://github.com/FortAwesome/react-fontawesome#basic
8
+ *
9
+ * 支持 fontawesome 所有 free-solid-icons,可以在这里查看所有图标 https://fontawesome.com/icons?d=gallery&s=solid&m=free
10
+ * ### 引用方法
11
+ *
12
+ * ~~~js
13
+ * import { Icon } from 'vikingship'
14
+ * ~~~
15
+ */
16
+ export const Icon = (props) => {
17
+ // icon-primary
18
+ const { className, theme, ...restProps } = props;
19
+ const classes = classNames('viking-icon', className, {
20
+ [`icon-${theme}`]: theme
21
+ });
22
+ return (_jsx(FontAwesomeIcon, { className: classes, ...restProps }));
23
+ };
24
+ export default Icon;
@@ -0,0 +1,6 @@
1
+ import type { FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
2
+ export type ThemeProps = 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'danger' | 'light' | 'dark';
3
+ export interface IconProps extends FontAwesomeIconProps {
4
+ /** 支持框架主题,根据主题显示不同的颜色 */
5
+ theme?: ThemeProps;
6
+ }
@@ -0,0 +1,2 @@
1
+ // Icon 组件的类型定义拆分到单独文件
2
+ export {};
@@ -0,0 +1,2 @@
1
+ import Icon from './icon';
2
+ export default Icon;
@@ -0,0 +1,2 @@
1
+ import Icon from './icon';
2
+ export default Icon;
@@ -0,0 +1,4 @@
1
+ import Input from './input';
2
+ export default Input;
3
+ export { Input } from './input';
4
+ export type * from './input.types';
@@ -0,0 +1,3 @@
1
+ import Input from './input';
2
+ export default Input;
3
+ export { Input } from './input';
@@ -0,0 +1,5 @@
1
+ import type { FC } from 'react';
2
+ import type { InputProps } from './input.types';
3
+ export type { InputProps, InputSize } from './input.types';
4
+ export declare const Input: FC<InputProps>;
5
+ export default Input;
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import classNames from 'classnames';
4
+ import Icon from '../Icon/icon';
5
+ export const Input = (props) => {
6
+ const { disabled, size, icon, prepend, append, style, onChange, ...restProps } = props;
7
+ const classes = useMemo(() => classNames('viking-input-wrapper', {
8
+ [`input-size-${size}`]: size,
9
+ 'input-group': prepend || append,
10
+ 'input-group-prepend': !!prepend,
11
+ 'input-group-append': !!append,
12
+ }), [append, prepend, size]);
13
+ const inputClasses = classNames('viking-input-inner', {
14
+ 'is-disabled': disabled,
15
+ });
16
+ const handleChange = (e) => {
17
+ if (onChange)
18
+ onChange(e);
19
+ };
20
+ const renderPrepend = () => {
21
+ if (!prepend)
22
+ return null;
23
+ return _jsx("div", { className: "viking-input-group-prepend", children: prepend });
24
+ };
25
+ const renderAppend = () => {
26
+ if (!append && !icon)
27
+ return null;
28
+ return (_jsxs("div", { className: "viking-input-group-append", children: [append, icon ? (_jsx("div", { className: "icon-wrapper", children: _jsx(Icon, { icon: icon }) })) : null] }));
29
+ };
30
+ return (_jsxs("div", { className: classes, style: style, children: [renderPrepend(), _jsx("input", { className: inputClasses, disabled: disabled, onChange: handleChange, ...restProps }), renderAppend()] }));
31
+ };
32
+ export default Input;