china-diff 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.
Files changed (52) hide show
  1. package/.gitattributes +1 -0
  2. package/.prettierignore +2 -0
  3. package/.prettierrc +5 -0
  4. package/README.md +1 -0
  5. package/build/css.ts +293 -0
  6. package/build/icons.ts +108 -0
  7. package/css/all.css +8 -0
  8. package/css/atomic/align.css +35 -0
  9. package/css/atomic/all.css +13 -0
  10. package/css/atomic/cursor.css +7 -0
  11. package/css/atomic/display.css +11 -0
  12. package/css/atomic/flex.css +124 -0
  13. package/css/atomic/grid.css +0 -0
  14. package/css/atomic/hidden.css +37 -0
  15. package/css/atomic/other.css +23 -0
  16. package/css/atomic/overflow.css +47 -0
  17. package/css/atomic/position.css +58 -0
  18. package/css/base.css +16 -0
  19. package/css/carousel.css +46 -0
  20. package/css/combobox.css +58 -0
  21. package/css/icon.css +7 -0
  22. package/icons/backward.svg +3 -0
  23. package/icons/close.svg +3 -0
  24. package/icons/dropdown.svg +3 -0
  25. package/icons/forward.svg +3 -0
  26. package/icons/toggle.svg +3 -0
  27. package/index.ts +1 -0
  28. package/jsx-runtime.d.ts +1 -0
  29. package/package.json +13 -0
  30. package/src/animate-scroll-to.ts +65 -0
  31. package/src/components/Carousel.tsx +299 -0
  32. package/src/components/CollapsiblePanel.tsx +139 -0
  33. package/src/components/ComboBox.tsx +163 -0
  34. package/src/components/Dialog.tsx +36 -0
  35. package/src/components/For.tsx +26 -0
  36. package/src/components/Icon.tsx +39 -0
  37. package/src/components/If.tsx +14 -0
  38. package/src/components/KeepAlive.tsx +26 -0
  39. package/src/components/Pulldown.tsx +157 -0
  40. package/src/components/Switch.tsx +49 -0
  41. package/src/dom.ts +137 -0
  42. package/src/http.ts +282 -0
  43. package/src/index.ts +68 -0
  44. package/src/jsx.ts +2 -0
  45. package/src/language.ts +34 -0
  46. package/src/layout.ts +89 -0
  47. package/src/location.ts +133 -0
  48. package/src/message.ts +290 -0
  49. package/src/reactive.ts +211 -0
  50. package/src/template.ts +23 -0
  51. package/tsconfig.json +16 -0
  52. package/vite-plugin.js +4 -0
package/src/dom.ts ADDED
@@ -0,0 +1,137 @@
1
+ export const isBrowser = typeof window !== 'undefined';
2
+
3
+ export const addEventListener: typeof document.addEventListener = isBrowser
4
+ ? document.addEventListener.bind(document)
5
+ : () => {};
6
+
7
+ export const removeEventListener: typeof document.removeEventListener = isBrowser
8
+ ? document.removeEventListener.bind(document)
9
+ : () => {};
10
+
11
+ /**
12
+ * 显示遮罩层的数量
13
+ */
14
+ let maskLayers: Function[] = [];
15
+ /**
16
+ * 遮罩层
17
+ */
18
+ let maskLayer: HTMLElement;
19
+
20
+ if (isBrowser) {
21
+ const root = document.documentElement;
22
+
23
+ const disableGlobalScroll = (event) => {
24
+ if (!maskLayers.length) {
25
+ } else {
26
+ let target = event.target as HTMLElement;
27
+
28
+ while (target && target !== root) {
29
+ if (
30
+ (window.getComputedStyle(target).overflow !== 'hidden' && target.scrollWidth > target.clientWidth) ||
31
+ target.scrollHeight > target.clientHeight
32
+ ) {
33
+ // todo 未处理滚动条顶或底的问题
34
+ return;
35
+ }
36
+
37
+ target = target.parentNode as HTMLElement;
38
+ }
39
+
40
+ event.preventDefault();
41
+ return false;
42
+ }
43
+ };
44
+
45
+ root.addEventListener('wheel', disableGlobalScroll, { passive: false });
46
+ root.addEventListener('touchmove', disableGlobalScroll, { passive: false });
47
+ }
48
+
49
+ /**
50
+ * 显示遮罩层
51
+ */
52
+ export const showMaskLayer = (onclick?: () => void) => {
53
+ if (isBrowser) {
54
+ if (!maskLayers.length) {
55
+ if (!maskLayer) {
56
+ let div = (maskLayer = document.createElement('div'));
57
+
58
+ div.className = 'mask-layer';
59
+ div.style.cssText =
60
+ 'position:fixed;top:0;left:0;right:0;bottom:0;display:none;background:rgba(0,0,0,0.4);z-index:8';
61
+
62
+ // 点击遮罩层方法
63
+ div.addEventListener('click', (event) => {
64
+ let callbackFn = maskLayers[maskLayers.length - 1];
65
+
66
+ callbackFn && callbackFn(event);
67
+ });
68
+
69
+ document.body.appendChild(div);
70
+ }
71
+
72
+ maskLayer.style.display = 'block';
73
+ }
74
+
75
+ maskLayers.push(onclick);
76
+ }
77
+ };
78
+
79
+ /**
80
+ * 隐藏遮罩层
81
+ */
82
+ export const hideMaskLayer = () => {
83
+ if (isBrowser) {
84
+ maskLayers.pop();
85
+
86
+ if (!maskLayers.length) {
87
+ maskLayer.style.display = 'none';
88
+ }
89
+ }
90
+ };
91
+
92
+ const autocloseList = [];
93
+
94
+ /**
95
+ * 注册自动关闭方法(弹出层,菜单等)
96
+ *
97
+ * @param onclose 自动关闭方法
98
+ */
99
+ export const registerAutoClose = isBrowser
100
+ ? (onclose: () => void) => {
101
+ autocloseList.push(onclose);
102
+ }
103
+ : () => {};
104
+
105
+ /**
106
+ * 取消注册自动关闭方法(弹出层,菜单等)
107
+ *
108
+ * @param onclose 自动关闭方法
109
+ */
110
+ export const unregisterAutoClose = (onclose: () => void) => {
111
+ for (let i = autocloseList.length; i--; ) {
112
+ if (autocloseList[i] === onclose) {
113
+ autocloseList.splice(i, 1);
114
+ break;
115
+ }
116
+ }
117
+ };
118
+
119
+ /**
120
+ * 侦听自动关闭事件(一般设置到 solid-js 的 App 节点上,且只能侦听一次)
121
+ */
122
+ export const listenAutoCloseEvent = {
123
+ onpointerdown: () => {
124
+ for (let i = 0, l = autocloseList.length; i < l; i++) {
125
+ autocloseList[i]();
126
+ }
127
+ },
128
+ };
129
+
130
+ /**
131
+ * 禁止自动关闭事件
132
+ */
133
+ export const disableAutoCloseEvent = {
134
+ onpointerdown: (event: Event) => {
135
+ event.stopPropagation();
136
+ },
137
+ };
package/src/http.ts ADDED
@@ -0,0 +1,282 @@
1
+ /**
2
+ * http 拦截器
3
+ */
4
+ export const httpInterceptor = {
5
+ /**
6
+ * 请求拦截
7
+ */
8
+ request: (() => {}) as (url: string, options: RequestInit) => string | void,
9
+
10
+ /**
11
+ * 响应拦截(返回 Promise true 表示需要重新发送请求,返回 Promise false 抛出默认异常)
12
+ */
13
+ response: (() => {}) as (
14
+ response: Response,
15
+ options?: RequestInit,
16
+ preventLogon?: boolean,
17
+ ) => Promise<boolean> | void,
18
+ };
19
+
20
+ /**
21
+ * API响应结果
22
+ */
23
+ export interface Result<T> {
24
+ /**
25
+ * 响应代码
26
+ */
27
+ code: number | string;
28
+ /**
29
+ * 响应消息
30
+ */
31
+ message: string;
32
+ /**
33
+ * 响应数据
34
+ */
35
+ data: T;
36
+ }
37
+
38
+ /**
39
+ * http 响应结果
40
+ */
41
+ export type HttpResult<T> = Promise<Result<T>> & {
42
+ /**
43
+ * 获取返回响应结果 { code, message, data } 中的 data
44
+ */
45
+ get data(): Promise<T>;
46
+ };
47
+
48
+ // 扩展 data 属性
49
+ Object.defineProperty(Promise.prototype, 'data', {
50
+ configurable: true,
51
+
52
+ get() {
53
+ return this.then((result) => {
54
+ let data = result.data;
55
+
56
+ if (data !== void 0) {
57
+ return data;
58
+ }
59
+
60
+ return Promise.reject(result.code + ' ' + result.message);
61
+ });
62
+ },
63
+ });
64
+
65
+ const handleResponse = (response: Response, url: string, options?: RequestInit, preventLogon?: boolean) => {
66
+ // 响应拦截
67
+ let promise = httpInterceptor.response(response, options, preventLogon);
68
+
69
+ // 返回了异步对象
70
+ if (promise) {
71
+ // 返回状态为 true 表示需要重新发送请求
72
+ return promise.then((status) =>
73
+ status ? sendInternal(url, options, preventLogon) : Promise.reject(response.status + ' ' + response.statusText),
74
+ );
75
+ }
76
+
77
+ // 成功响应
78
+ if (response.ok) {
79
+ return response;
80
+ }
81
+
82
+ // 失败返回异常
83
+ return Promise.reject(response.status + ' ' + response.statusText);
84
+ };
85
+
86
+ /**
87
+ * 发送方法
88
+ *
89
+ * @param url 请求URL
90
+ * @param data 请求数据
91
+ * @param options 请求参数
92
+ */
93
+ let sendInternal = (url: string, options?: RequestInit, preventLogon?: boolean): Promise<Response> => {
94
+ return fetch(url, options).then((response) => handleResponse(response, url, options, preventLogon));
95
+ };
96
+
97
+ /**
98
+ * 自定义请求发送
99
+ *
100
+ * @param url 请求URL
101
+ * @param options 请求参数
102
+ */
103
+ const send = (url: string, options?: RequestInit, preventLogon?: boolean): Promise<Response> => {
104
+ return fetch(url, options).then((response) => handleResponse(response, url, options, preventLogon));
105
+ };
106
+
107
+ /**
108
+ * 自定义请求 JSON 数据的方法
109
+ *
110
+ * @param method 请求方法
111
+ * @param url 请求URL
112
+ * @param data 请求数据
113
+ * @param options 请求参数
114
+ */
115
+ const request = <T>(
116
+ method: string,
117
+ url: string,
118
+ data?: unknown,
119
+ options?: Omit<RequestInit, 'body'>,
120
+ preventLogon?: boolean,
121
+ ): HttpResult<T> => {
122
+ if (options) {
123
+ let headers = options.headers;
124
+
125
+ options.method = method;
126
+
127
+ if (headers ? !headers['Content-Type'] : (headers = options.headers = {})) {
128
+ headers['Content-Type'] = 'application/json';
129
+ }
130
+ } else {
131
+ options = {
132
+ method,
133
+ headers: {
134
+ 'Content-Type': 'application/json',
135
+ },
136
+ };
137
+ }
138
+
139
+ if (data !== void 0) {
140
+ (options as RequestInit).body = JSON.stringify(data);
141
+ }
142
+
143
+ url = httpInterceptor.request(url, options) || url;
144
+
145
+ return sendInternal(url, options, preventLogon).then((response) => response.json()) as HttpResult<T>;
146
+ };
147
+
148
+ /**
149
+ * GET请求获取 JSON 数据
150
+ *
151
+ * @param url 请求URL
152
+ * @param options 请求参数
153
+ * @example 取消示例
154
+ * import http from 'china-diff/http';
155
+ *
156
+ * const controller = new AbortController();
157
+ * const signal = controller.signal;
158
+ *
159
+ * // 3秒后未返回则取消
160
+ * setTimeout(() => controller.abort(), 3000);
161
+ * // 发送带取消信号的请求
162
+ * http.get('https://example.com/api/...', { signal });
163
+ */
164
+ const get = <T>(url: string, options?: Omit<RequestInit, 'method' | 'body'>, preventLogon?: boolean): HttpResult<T> => {
165
+ return request('GET', url, void 0, options, preventLogon);
166
+ };
167
+
168
+ /**
169
+ * POST请求获取 JSON 数据
170
+ *
171
+ * @param url 请求URL
172
+ * @param data 请求数据(JSON)
173
+ * @param options 请求参数
174
+ * @example 取消示例
175
+ * import http from 'china-diff/http';
176
+ *
177
+ * const controller = new AbortController();
178
+ * const signal = controller.signal;
179
+ *
180
+ * // 3秒后未返回则取消
181
+ * setTimeout(() => controller.abort(), 3000);
182
+ * // 发送带取消信号的请求
183
+ * http.post('https://example.com/api/...', null, { signal });
184
+ */
185
+ const post = <T>(
186
+ url: string,
187
+ data?: unknown,
188
+ options?: Omit<RequestInit, 'method' | 'body'>,
189
+ preventLogon?: boolean,
190
+ ): HttpResult<T> => {
191
+ return request('POST', url, data, options, preventLogon);
192
+ };
193
+
194
+ /**
195
+ * PUT请求获取 JSON 数据
196
+ *
197
+ * @param url 请求URL
198
+ * @param data 请求数据(JSON)
199
+ * @param options 请求参数
200
+ */
201
+ const put = <T>(
202
+ url: string,
203
+ data: unknown,
204
+ options?: Omit<RequestInit, 'method' | 'body'>,
205
+ preventLogon?: boolean,
206
+ ): HttpResult<T> => {
207
+ return request('PUT', url, data, options, preventLogon);
208
+ };
209
+
210
+ /**
211
+ * DELET请求获取 JSON 数据
212
+ *
213
+ * @param url 请求URL
214
+ * @param options 请求参数
215
+ */
216
+ const del = <T>(
217
+ url: string,
218
+ data?: unknown,
219
+ options?: Omit<RequestInit, 'method' | 'body'>,
220
+ preventLogon?: boolean,
221
+ ): HttpResult<T> => {
222
+ return request('DELETE', url, data, options, preventLogon);
223
+ };
224
+
225
+ /**
226
+ * 网络跟踪结果
227
+ */
228
+ export interface NetworkTrackingResult {
229
+ start: number;
230
+ end: number;
231
+ url: string;
232
+ options?: RequestInit;
233
+ response?: Response;
234
+ error?: any;
235
+ }
236
+
237
+ /**
238
+ * 跟踪网络请求
239
+ *
240
+ * @param callbackFn 回调处理
241
+ */
242
+ export const track = (callbackFn: (result: NetworkTrackingResult) => void) => {
243
+ sendInternal = (url: string, options?: RequestInit, preventLogon?: boolean): Promise<Response> => {
244
+ let start = Date.now();
245
+
246
+ return fetch(url, options)
247
+ .then((response) => {
248
+ callbackFn({
249
+ start,
250
+ end: Date.now(),
251
+ url,
252
+ options,
253
+ response,
254
+ });
255
+
256
+ return handleResponse(response, url, options, preventLogon);
257
+ })
258
+ .catch((error) => {
259
+ callbackFn({
260
+ start,
261
+ end: Date.now(),
262
+ url,
263
+ options,
264
+ error,
265
+ });
266
+
267
+ return Promise.reject(error);
268
+ });
269
+ };
270
+ };
271
+
272
+ /**
273
+ * http 请求
274
+ */
275
+ export const http = {
276
+ send,
277
+ request,
278
+ get,
279
+ post,
280
+ put,
281
+ del,
282
+ };
package/src/index.ts ADDED
@@ -0,0 +1,68 @@
1
+ export * from './message';
2
+ export * from './language';
3
+ export * from './http';
4
+
5
+ export {
6
+ type JSX,
7
+ createComponent,
8
+ createEffect,
9
+ createMemo,
10
+ createContext,
11
+ useContext,
12
+ splitProps,
13
+ onMount,
14
+ onCleanup,
15
+ untrack,
16
+ batch,
17
+ } from 'solid-js';
18
+
19
+ export { Show, For, hydrate, render } from 'solid-js/web';
20
+
21
+ export * from './dom';
22
+ export * from './animate-scroll-to';
23
+
24
+ export * from './reactive';
25
+ export * from './location';
26
+ export * from './layout';
27
+
28
+ export * from './components/If';
29
+ export * from './components/Switch';
30
+ export * from './components/For';
31
+ export * from './components/Icon';
32
+ export * from './components/CollapsiblePanel';
33
+ export * from './components/ComboBox';
34
+ export * from './components/Carousel';
35
+ export * from './components/KeepAlive';
36
+ export * from './components/Dialog';
37
+
38
+ declare global {
39
+ export interface ImportMeta {
40
+ // 配置环境变量类型
41
+ env: {
42
+ /**
43
+ * 应用运行模式
44
+ */
45
+ MODE: string;
46
+
47
+ /**
48
+ * 是否服务端渲染
49
+ */
50
+ SSR: boolean;
51
+
52
+ /**
53
+ * 是否浏览器模式
54
+ */
55
+ VITE_BROWSER: boolean;
56
+
57
+ /**
58
+ * API基础路径
59
+ */
60
+ VITE_API_BASE_URL: string;
61
+
62
+ /**
63
+ * 是否开启 API Mock
64
+ */
65
+ VITE_API_MOCK: boolean;
66
+ };
67
+ }
68
+ }
package/src/jsx.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { type JSX } from 'solid-js';
2
+
@@ -0,0 +1,34 @@
1
+ /**
2
+ * 修复语言大小写问题(有些浏览器会把 zh-CN 写成 zh-cn)
3
+ *
4
+ * @param name 语言名称
5
+ */
6
+ const fixLanguageName = (name: string) => {
7
+ let index = name.indexOf('-');
8
+
9
+ if (index < 0) {
10
+ index = name.indexOf('_');
11
+ }
12
+
13
+ if (index > 0) {
14
+ return name.slice(0, index) + '-' + name.slice(index + 1).toUpperCase();
15
+ }
16
+
17
+ return name;
18
+ };
19
+
20
+ /**
21
+ * 获取当前语言名称
22
+ */
23
+ export const getLanguage = (): string => {
24
+ return localStorage.getItem('LANGUAGE') || fixLanguageName(navigator.language) || 'en';
25
+ };
26
+
27
+ /**
28
+ * 保存当前语言名称
29
+ *
30
+ * @param name 语言名
31
+ */
32
+ export const saveLanguage = (name: string) => {
33
+ localStorage.setItem('LANGUAGE', name ? fixLanguageName(name) : 'en');
34
+ };
package/src/layout.ts ADDED
@@ -0,0 +1,89 @@
1
+ import { isBrowser } from './dom';
2
+ import { reactive } from './reactive';
3
+
4
+ /**
5
+ * 当前布局
6
+ */
7
+ // export const layout = reactive({
8
+ // pc: true,
9
+ // tablet: false,
10
+ // mobile: false,
11
+ // landscape: true,
12
+ // 'gt-1920': false,
13
+ // 'gt-1280': false,
14
+ // 'gt-1024': true,
15
+ // 'gt-800': true,
16
+ // 'gt-640': true,
17
+ // 'gt-480': true,
18
+
19
+ // 'ge-1920': false,
20
+ // 'ge-1280': true,
21
+ // 'ge-1024': true,
22
+ // 'ge-800': true,
23
+ // 'ge-640': true,
24
+ // 'ge-480': true,
25
+
26
+ // 'le-1920': true,
27
+ // 'le-1280': true,
28
+ // 'le-1024': false,
29
+ // 'le-800': false,
30
+ // 'le-640': false,
31
+ // 'le-480': false,
32
+
33
+ // 'lt-1920': true,
34
+ // 'lt-1280': false,
35
+ // 'lt-1024': false,
36
+ // 'lt-800': false,
37
+ // 'lt-640': false,
38
+ // 'lt-480': false,
39
+ // });
40
+ // 默认按手机端展示
41
+ export const layout = reactive({
42
+ pc: true,
43
+ tablet: false,
44
+ mobile: false,
45
+ landscape: true,
46
+ 'gt-1920': false,
47
+ 'gt-1280': false,
48
+ 'gt-1024': false,
49
+ 'gt-800': false,
50
+ 'gt-640': false,
51
+ 'gt-480': false,
52
+
53
+ 'ge-1920': false,
54
+ 'ge-1280': false,
55
+ 'ge-1024': false,
56
+ 'ge-800': false,
57
+ 'ge-640': false,
58
+ 'ge-480': true,
59
+
60
+ 'le-1920': true,
61
+ 'le-1280': true,
62
+ 'le-1024': true,
63
+ 'le-800': true,
64
+ 'le-640': true,
65
+ 'le-480': true,
66
+
67
+ 'lt-1920': true,
68
+ 'lt-1280': true,
69
+ 'lt-1024': true,
70
+ 'lt-800': true,
71
+ 'lt-640': true,
72
+ 'lt-480': false,
73
+ });
74
+
75
+ const update = () => {
76
+ let classList = document.body.classList;
77
+ let names = Object.getOwnPropertyNames(layout);
78
+
79
+ for (let i = 0, l = names.length; i < l; i++) {
80
+ layout[names[i]] = classList.contains(names[i]);
81
+ }
82
+ };
83
+
84
+ if (isBrowser) {
85
+ // 更新布局
86
+ update();
87
+ // 订阅布局发生变化事件(由 index.html 发出)
88
+ (window as any).onlayoutchange = update;
89
+ }