cdui-js 1.0.22 → 1.0.24
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/css/all.css +1 -2
- package/css/datewidget.css +9 -0
- package/css/{datepicker.css → dropdown.css} +6 -10
- package/css/popup.css +38 -19
- package/demo/css/css.md +6 -10
- package/demo/src/App.tsx +22 -2
- package/demo/src/css/atomic.css +6 -8
- package/demo/src/pages/Canlendar.tsx +4 -4
- package/demo/src/pages/ComboBox.tsx +17 -14
- package/demo/src/pages/DatePicker.tsx +6 -1
- package/demo/src/pages/Form.tsx +70 -45
- package/package.json +1 -1
- package/src/components/Canlendar.tsx +91 -48
- package/src/components/ComboBox.tsx +57 -51
- package/src/components/DatePicker.tsx +146 -60
- package/src/components/Form.tsx +1 -0
- package/src/components/MonthPicker.tsx +1 -1
- package/src/components/MonthWidget.tsx +65 -36
- package/src/components/YearPicker.tsx +1 -1
- package/src/components/YearWidget.tsx +62 -138
- package/src/{components/Dialog.ts → dialog.ts} +13 -6
- package/src/i18n/index.ts +2 -2
- package/src/index.ts +4 -2
- package/src/popup.ts +341 -0
- package/src/reactive.ts +4 -1
- package/src/ssr/render.ts +5 -5
- package/css/combobox.css +0 -38
- package/demo/src/pages/MobileDatePicker.tsx +0 -5
- package/src/components/MobileDatePicker.tsx +0 -444
- package/src/components/Popup.tsx +0 -228
package/src/popup.ts
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { createRoot } from 'solid-js';
|
|
2
|
+
|
|
3
|
+
import { JSX } from './jsx';
|
|
4
|
+
import { disableAutoCloseEvent, hideMaskLayer, registerAutoClose, showMaskLayer, unregisterAutoClose } from './dom';
|
|
5
|
+
import { layout } from './layout';
|
|
6
|
+
|
|
7
|
+
const disableAutoClose = disableAutoCloseEvent.onpointerdown;
|
|
8
|
+
|
|
9
|
+
const alignPopups: [
|
|
10
|
+
style: CSSStyleDeclaration,
|
|
11
|
+
alignTarget: HTMLElement,
|
|
12
|
+
left: number,
|
|
13
|
+
top: number,
|
|
14
|
+
x: number,
|
|
15
|
+
y: number,
|
|
16
|
+
][] = [];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 打开弹出层选项
|
|
20
|
+
*/
|
|
21
|
+
export interface PopupOptions {
|
|
22
|
+
/**
|
|
23
|
+
* 弹出方向 (位置不够时自动向反方向弹出)
|
|
24
|
+
*/
|
|
25
|
+
direction?: 'bottom' | 'top' | 'right' | 'left';
|
|
26
|
+
/**
|
|
27
|
+
* 是否与指定组件对齐(不设置则从屏幕的指定方向弹出)
|
|
28
|
+
*/
|
|
29
|
+
align?: HTMLElement;
|
|
30
|
+
/**
|
|
31
|
+
* 是否与对齐组件同宽
|
|
32
|
+
*/
|
|
33
|
+
alignWidth?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* 是否反向对齐(仅设置了 align 时有效)
|
|
36
|
+
*/
|
|
37
|
+
reverse?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* 是否启用过渡动画
|
|
40
|
+
*/
|
|
41
|
+
transition?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* 是否显示遮罩层(不设置时默认页面小于等于 480px 显示遮罩层)
|
|
44
|
+
*/
|
|
45
|
+
mask?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* 关闭时是否销毁(默认销毁)
|
|
48
|
+
*/
|
|
49
|
+
destroy?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* 打开弹出层事件
|
|
52
|
+
*/
|
|
53
|
+
onopen?: () => void;
|
|
54
|
+
/**
|
|
55
|
+
* 关闭弹出层事件
|
|
56
|
+
*/
|
|
57
|
+
onclose?: () => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 弹出层
|
|
62
|
+
*/
|
|
63
|
+
export type Popup = HTMLElement & {
|
|
64
|
+
/**
|
|
65
|
+
* 是否打开状态
|
|
66
|
+
*/
|
|
67
|
+
opened: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* 关闭弹出层
|
|
70
|
+
*/
|
|
71
|
+
close(destroy?: boolean): void;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
document.addEventListener(
|
|
75
|
+
'scroll',
|
|
76
|
+
() => {
|
|
77
|
+
for (let i = 0, l = alignPopups.length; i < l; i++) {
|
|
78
|
+
let [style, alignTarget, left, top, x, y] = alignPopups[i];
|
|
79
|
+
let rect = alignTarget.getBoundingClientRect();
|
|
80
|
+
|
|
81
|
+
style.left = x + (rect.left - left) + 'px';
|
|
82
|
+
style.top = y + (rect.top - top) + 'px';
|
|
83
|
+
style.bottom = '';
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
true,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const computeAlign = (
|
|
90
|
+
host: HTMLElement,
|
|
91
|
+
popup: Popup,
|
|
92
|
+
alignTarget: HTMLElement,
|
|
93
|
+
direction: 'bottom' | 'top' | 'right' | 'left',
|
|
94
|
+
reverse: boolean,
|
|
95
|
+
) => {
|
|
96
|
+
let style = host.style;
|
|
97
|
+
|
|
98
|
+
let rect = alignTarget.getBoundingClientRect();
|
|
99
|
+
let alignWidth = rect.width;
|
|
100
|
+
let alignHeight = rect.height;
|
|
101
|
+
|
|
102
|
+
let popupWidth = popup.offsetWidth;
|
|
103
|
+
let popupHeight = popup.offsetHeight;
|
|
104
|
+
let windowWidth = window.innerWidth;
|
|
105
|
+
let windowHeight = window.innerHeight;
|
|
106
|
+
|
|
107
|
+
let left = rect.left;
|
|
108
|
+
let top = rect.top;
|
|
109
|
+
let x: number;
|
|
110
|
+
let y: number;
|
|
111
|
+
let is: boolean;
|
|
112
|
+
|
|
113
|
+
if ((is = direction === 'bottom') || direction === 'top') {
|
|
114
|
+
// 向下弹出
|
|
115
|
+
if (is) {
|
|
116
|
+
y = top + alignHeight + 2;
|
|
117
|
+
|
|
118
|
+
// 下面空间不够且上面的空间大于下面的空间
|
|
119
|
+
if (windowHeight - y < popupHeight && windowHeight - y < top - 2) {
|
|
120
|
+
// 向上弹出
|
|
121
|
+
is = false;
|
|
122
|
+
y = top - 2 - popupHeight;
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
// 向上弹出
|
|
126
|
+
y = top - 2 - popupHeight;
|
|
127
|
+
|
|
128
|
+
// 上面空间不够且下面的空间大于上面的空间
|
|
129
|
+
if (top - 2 < popupHeight && y - 2 < windowHeight - y) {
|
|
130
|
+
// 向下弹出
|
|
131
|
+
is = true;
|
|
132
|
+
y = top + alignHeight + 2;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (reverse) {
|
|
137
|
+
// 右对齐
|
|
138
|
+
x = left + alignWidth - popupWidth;
|
|
139
|
+
|
|
140
|
+
// 右边空间不够且左边空间大于右边空间
|
|
141
|
+
} else {
|
|
142
|
+
// 左对齐
|
|
143
|
+
x = left;
|
|
144
|
+
|
|
145
|
+
// 左边空间不够且右边空间大于左边空间
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
style.left = x + 'px';
|
|
149
|
+
style.right = '';
|
|
150
|
+
style.top = is ? y + 'px' : '';
|
|
151
|
+
style.bottom = is ? '' : windowHeight - (top - 2) + 'px';
|
|
152
|
+
} else {
|
|
153
|
+
// 是否向右弹出
|
|
154
|
+
is = direction === 'right';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 注册滚动对齐侦听
|
|
158
|
+
alignPopups.push([style, alignTarget, left, top, x, y]);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const openPopup = (popup: Popup, options: PopupOptions) => {
|
|
162
|
+
let align = options.align;
|
|
163
|
+
let direction = options.direction || 'bottom';
|
|
164
|
+
let host = popup.parentNode as HTMLElement;
|
|
165
|
+
let style = host.style;
|
|
166
|
+
|
|
167
|
+
host.className = `popup popup-${align ? 'align' : 'fixed'}-${direction}`;
|
|
168
|
+
|
|
169
|
+
if (align) {
|
|
170
|
+
// 没有指定宽度
|
|
171
|
+
if (options.alignWidth !== false && !host.style.width) {
|
|
172
|
+
host.style.width = align.offsetWidth + 'px';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
computeAlign(host, popup, align, direction, options.reverse);
|
|
176
|
+
} else {
|
|
177
|
+
style.left = style.top = style.right = style.bottom = '';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (options.mask) {
|
|
181
|
+
showMaskLayer(() => closePopup(popup, options));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 设置为打开状态
|
|
185
|
+
popup.opened = true;
|
|
186
|
+
// 触发打开事件
|
|
187
|
+
options.onopen && options.onopen();
|
|
188
|
+
|
|
189
|
+
// 过渡动画
|
|
190
|
+
if (options.transition) {
|
|
191
|
+
let height = popup.offsetHeight;
|
|
192
|
+
|
|
193
|
+
style.height = '0';
|
|
194
|
+
|
|
195
|
+
setTimeout(() => {
|
|
196
|
+
style.height = height + 'px';
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const closePopup = (popup: Popup, options: PopupOptions) => {
|
|
202
|
+
let body = document.body;
|
|
203
|
+
let parent = popup.parentNode as HTMLElement;
|
|
204
|
+
let align;
|
|
205
|
+
|
|
206
|
+
if (options.mask) {
|
|
207
|
+
hideMaskLayer();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 注销滚动对齐侦听
|
|
211
|
+
if ((align = options.align)) {
|
|
212
|
+
for (let i = alignPopups.length; i--; ) {
|
|
213
|
+
if (alignPopups[i][0] === align) {
|
|
214
|
+
alignPopups.splice(i, 1);
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// 设置为关闭状态
|
|
221
|
+
popup.opened = false;
|
|
222
|
+
// 触发关闭事件
|
|
223
|
+
options.onclose && options.onclose();
|
|
224
|
+
|
|
225
|
+
// 过渡动画
|
|
226
|
+
if (options.transition) {
|
|
227
|
+
let style = parent.style;
|
|
228
|
+
|
|
229
|
+
style.height = popup.offsetHeight + 'px';
|
|
230
|
+
|
|
231
|
+
setTimeout(() => {
|
|
232
|
+
style.height = '0';
|
|
233
|
+
});
|
|
234
|
+
} else {
|
|
235
|
+
body.removeChild(parent);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* 打开弹出层
|
|
241
|
+
*
|
|
242
|
+
* @param component 要弹出的组件
|
|
243
|
+
* @param options 打开弹出层选项
|
|
244
|
+
*/
|
|
245
|
+
export const showPopup = (component: JSX.Element | (() => JSX.Element), options?: PopupOptions): Popup => {
|
|
246
|
+
return createRoot((dispose) => {
|
|
247
|
+
let body = document.body;
|
|
248
|
+
let popup = (typeof component === 'function' ? component() : component) as Popup;
|
|
249
|
+
let host = document.createElement('div');
|
|
250
|
+
|
|
251
|
+
const close = (popup.close = () => {
|
|
252
|
+
unregisterAutoClose(close);
|
|
253
|
+
closePopup(popup, options);
|
|
254
|
+
|
|
255
|
+
options.destroy !== false && dispose();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
host.onpointerdown = disableAutoClose;
|
|
259
|
+
host.ontransitionend = () => {
|
|
260
|
+
host.style.height = 'auto';
|
|
261
|
+
popup.opened || body.removeChild(host);
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
host.appendChild(popup);
|
|
265
|
+
body.appendChild(host);
|
|
266
|
+
|
|
267
|
+
openPopup(popup, options || (options = {}));
|
|
268
|
+
|
|
269
|
+
registerAutoClose(close);
|
|
270
|
+
|
|
271
|
+
return popup;
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
let dropdownTransition = true;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* 设置下拉框是否使用过渡动画
|
|
279
|
+
*
|
|
280
|
+
* @param transition 是否使用过渡动画
|
|
281
|
+
*/
|
|
282
|
+
export const setDropdownTransition = (transition: boolean) => {
|
|
283
|
+
dropdownTransition = transition;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* 弹出层外部调用接口
|
|
288
|
+
*/
|
|
289
|
+
export interface PopupApi {
|
|
290
|
+
/**
|
|
291
|
+
* 打开弹出层
|
|
292
|
+
*/
|
|
293
|
+
openPopup(): void;
|
|
294
|
+
/**
|
|
295
|
+
* 关闭弹出层
|
|
296
|
+
*/
|
|
297
|
+
closePopup(): void;
|
|
298
|
+
/**
|
|
299
|
+
* 打开或关闭弹出层
|
|
300
|
+
*/
|
|
301
|
+
togglePopup(): void;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* 初始化弹出层
|
|
306
|
+
*
|
|
307
|
+
* @param dom 对齐组件
|
|
308
|
+
* @param popupTarget 弹出目标组件
|
|
309
|
+
* @param options 指定弹出参数
|
|
310
|
+
*/
|
|
311
|
+
export const initDropdown = (dom: HTMLElement, popupTarget: () => JSX.Element, options?: PopupOptions): PopupApi => {
|
|
312
|
+
let popup: Popup;
|
|
313
|
+
|
|
314
|
+
const openPopup = () => {
|
|
315
|
+
popup ||
|
|
316
|
+
(popup = showPopup(popupTarget(), {
|
|
317
|
+
align: layout['gt-480'] && dom,
|
|
318
|
+
onclose: () => (popup = null),
|
|
319
|
+
transition: dropdownTransition,
|
|
320
|
+
...options,
|
|
321
|
+
}));
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const closePopup = () => {
|
|
325
|
+
popup && popup.close();
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const togglePopup = () => {
|
|
329
|
+
if (popup) {
|
|
330
|
+
popup.close();
|
|
331
|
+
} else {
|
|
332
|
+
openPopup();
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
openPopup,
|
|
338
|
+
closePopup,
|
|
339
|
+
togglePopup,
|
|
340
|
+
};
|
|
341
|
+
};
|
package/src/reactive.ts
CHANGED
|
@@ -190,6 +190,9 @@ function arrayProxyGetHandler(target: any, property: any, receiver) {
|
|
|
190
190
|
|
|
191
191
|
// 拦截索引访问
|
|
192
192
|
if (index >= 0) {
|
|
193
|
+
// 收集依赖
|
|
194
|
+
this.signal[0]();
|
|
195
|
+
|
|
193
196
|
// 简单值
|
|
194
197
|
if (typeof value !== 'object' || !value) {
|
|
195
198
|
return value;
|
|
@@ -277,7 +280,7 @@ const reactiveObject = (object: object) => {
|
|
|
277
280
|
signal[0]();
|
|
278
281
|
|
|
279
282
|
// 值类型
|
|
280
|
-
if (typeof value !== 'object' || !value) {
|
|
283
|
+
if (typeof value !== 'object' || !value || value instanceof Date) {
|
|
281
284
|
return value;
|
|
282
285
|
}
|
|
283
286
|
|
package/src/ssr/render.ts
CHANGED
|
@@ -69,7 +69,7 @@ const renderToStringAsync = async <T>(context: ServerContext, fn: () => T) => {
|
|
|
69
69
|
*/
|
|
70
70
|
const renderPage = async (
|
|
71
71
|
App: Component<any>,
|
|
72
|
-
|
|
72
|
+
i18n: { [key: string]: any },
|
|
73
73
|
language: string,
|
|
74
74
|
root: string,
|
|
75
75
|
template: string,
|
|
@@ -90,8 +90,8 @@ const renderPage = async (
|
|
|
90
90
|
if (language !== 'en') {
|
|
91
91
|
template = template.replace('lang="en"', 'lang="' + language + '"');
|
|
92
92
|
|
|
93
|
-
if (
|
|
94
|
-
scripts.push('<script type="text/javascript">window.I18N=' + JSON.stringify(
|
|
93
|
+
if (i18n[language]) {
|
|
94
|
+
scripts.push('<script type="text/javascript">window.I18N=' + JSON.stringify(i18n[language]) + '</script>');
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -138,7 +138,7 @@ export const SSR_ERRORS = [];
|
|
|
138
138
|
*/
|
|
139
139
|
export const renderSSRPages = async (
|
|
140
140
|
App: Component<any>,
|
|
141
|
-
|
|
141
|
+
i18n: { [key: string]: any },
|
|
142
142
|
language: string,
|
|
143
143
|
root: string,
|
|
144
144
|
template: string,
|
|
@@ -154,7 +154,7 @@ export const renderSSRPages = async (
|
|
|
154
154
|
|
|
155
155
|
console.log(`rendering:${language} ${page.path}`);
|
|
156
156
|
|
|
157
|
-
await renderPage(App,
|
|
157
|
+
await renderPage(App, i18n, language, root, template, page);
|
|
158
158
|
|
|
159
159
|
console.log(`rendered: ${language} ${page.path} time: ${Date.now() - now}`);
|
|
160
160
|
} catch (err) {
|
package/css/combobox.css
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
.combobox {
|
|
2
|
-
position: relative;
|
|
3
|
-
width: 100px;
|
|
4
|
-
height: 32px;
|
|
5
|
-
overflow: visible;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.combobox-host {
|
|
9
|
-
display: flex;
|
|
10
|
-
justify-content: center;
|
|
11
|
-
align-items: center;
|
|
12
|
-
width: 100%;
|
|
13
|
-
height: 100%;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.combobox-input {
|
|
17
|
-
flex: auto;
|
|
18
|
-
width: 100%;
|
|
19
|
-
height: 100%;
|
|
20
|
-
outline: none;
|
|
21
|
-
border: none;
|
|
22
|
-
padding: 0 4px;
|
|
23
|
-
background: inherit;
|
|
24
|
-
color: inherit;
|
|
25
|
-
vertical-align: top;
|
|
26
|
-
font-size: inherit;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.combobox-host > .icon {
|
|
30
|
-
display: flex;
|
|
31
|
-
justify-items: center;
|
|
32
|
-
align-items: center;
|
|
33
|
-
height: 100%;
|
|
34
|
-
padding: 0 4px;
|
|
35
|
-
stroke: inherit;
|
|
36
|
-
fill: inherit;
|
|
37
|
-
cursor: pointer;
|
|
38
|
-
}
|