cdui-js 1.0.14 → 1.0.16
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 +52 -6
- package/css/all.css +1 -0
- package/css/mobile-datepicker.css +75 -0
- package/demo/src/App.tsx +23 -0
- package/demo/src/pages/MobileDatePicker.tsx +5 -0
- package/package.json +1 -1
- package/src/components/Canlendar.tsx +18 -19
- package/src/components/Carousel.tsx +18 -21
- package/src/components/CollapsiblePanel.tsx +14 -20
- package/src/components/ComboBox.tsx +11 -19
- package/src/components/DatePicker.tsx +9 -8
- package/src/components/Form.tsx +28 -28
- package/src/components/Icon.tsx +5 -7
- package/src/components/MobileDatePicker.tsx +444 -0
- package/src/components/MonthPicker.tsx +22 -0
- package/src/components/MonthWidget.tsx +41 -0
- package/src/components/Popup.tsx +6 -5
- package/src/components/Pulldown.tsx +4 -3
- package/src/components/TextBox.tsx +4 -4
- package/src/components/YearPicker.tsx +22 -0
- package/src/components/YearWidget.tsx +22 -0
- package/src/http.ts +3 -3
- package/src/index.ts +5 -0
- package/src/reactive.ts +36 -0
package/README.md
CHANGED
|
@@ -100,6 +100,40 @@ console.log(getValue());
|
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
|
|
103
|
+
## 批量更新
|
|
104
|
+
|
|
105
|
+
同一时刻如果修改了多个响应式属性,则每次修改都会触发响应式更新。
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
import { batch, createEffect, reactive } from 'cdui-js';
|
|
109
|
+
|
|
110
|
+
const state = reactive({ value1: 1, value2: 2 });
|
|
111
|
+
|
|
112
|
+
createEffect(() => {
|
|
113
|
+
console.log(state.value1, state.value2);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// 两次更新会执行 createEffect 回调两次
|
|
117
|
+
state.value1++;
|
|
118
|
+
state.value2++;
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
这种情况可使用`batch`进行批量修改响应式属性以减少响应式自动更新的次数。
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
import { batch, createEffect, reactive } from 'cdui-js';
|
|
125
|
+
|
|
126
|
+
const state = reactive({ value1: 1, value2: 2 });
|
|
127
|
+
|
|
128
|
+
createEffect(() => {
|
|
129
|
+
console.log(state.value1, state.value2);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// 两次更新会执行 createEffect 回调两次
|
|
133
|
+
state.value1++;
|
|
134
|
+
state.value2++;
|
|
135
|
+
```
|
|
136
|
+
|
|
103
137
|
# 响应式组件
|
|
104
138
|
|
|
105
139
|
任意一个函数,如果返回了`JSX.Element`,则这个函数就是一个响应式组件。建议使用`tsx`作为组件模板,本响应式框架没有虚拟`DOM`,也更轻量高效。
|
|
@@ -257,17 +291,17 @@ function ParentComponent() {
|
|
|
257
291
|
}
|
|
258
292
|
```
|
|
259
293
|
|
|
260
|
-
有时候,需要把不同的属性值应用到不同的`DOM`节点,可以使用`
|
|
294
|
+
有时候,需要把不同的属性值应用到不同的`DOM`节点,可以使用`omitProps`方法排除`props`中不需要的属性。`omitProps`返回的对象仍具有响应式特性。
|
|
261
295
|
|
|
262
296
|
```tsx
|
|
263
|
-
import {
|
|
297
|
+
import { omitProps } from 'cdui-js';
|
|
264
298
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const [titleProps, childrenProps, restProps] = splitProps(props, ['title'], ['children']);
|
|
299
|
+
// 定义要排除的属性集合(使用 as const 标记为元组)
|
|
300
|
+
const OMIT_PROPS = ['title', 'children'] as const;
|
|
268
301
|
|
|
302
|
+
function Component(props: { title: string, children: JSX.Element[] }) {
|
|
269
303
|
return (
|
|
270
|
-
<div {...
|
|
304
|
+
<div {...omitProps(props, OMIT_PROPS)}>
|
|
271
305
|
<div>{titleProps.title}</div>
|
|
272
306
|
<div>{childrenProps.children}</div>
|
|
273
307
|
</div>
|
|
@@ -275,6 +309,18 @@ function Component(props: { title: string, children: JSX.Element[] }) {
|
|
|
275
309
|
}
|
|
276
310
|
```
|
|
277
311
|
|
|
312
|
+
可以使用`combineClass`合并`class`:
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
import { combineClass } from 'cdui-js';
|
|
316
|
+
|
|
317
|
+
function Component(props: { class: string }) {
|
|
318
|
+
return (
|
|
319
|
+
<div class={combineClass('defaultClassName', props.class)}></div>
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
278
324
|
> 注意,解构`props`对象会丧失响应性,除非你确定要这样,否则不要随意解构任意`props`或响应式对象。
|
|
279
325
|
|
|
280
326
|
```tsx
|
package/css/all.css
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
.mobile-date-picker {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: row;
|
|
4
|
+
width: 100%;
|
|
5
|
+
min-height: calc(44px * 5);
|
|
6
|
+
position: relative;
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
user-select: none;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.mobile-date-picker-column {
|
|
12
|
+
flex: 1;
|
|
13
|
+
position: relative;
|
|
14
|
+
min-width: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.mobile-date-picker-column::before {
|
|
18
|
+
content: '';
|
|
19
|
+
pointer-events: none;
|
|
20
|
+
position: absolute;
|
|
21
|
+
left: 0;
|
|
22
|
+
right: 0;
|
|
23
|
+
top: 0;
|
|
24
|
+
bottom: 0;
|
|
25
|
+
z-index: 1;
|
|
26
|
+
background: linear-gradient(
|
|
27
|
+
to bottom,
|
|
28
|
+
rgba(255, 255, 255, 0.95) 0%,
|
|
29
|
+
rgba(255, 255, 255, 0) calc(50% - 22px)
|
|
30
|
+
),
|
|
31
|
+
linear-gradient(
|
|
32
|
+
to top,
|
|
33
|
+
rgba(255, 255, 255, 0.95) 0%,
|
|
34
|
+
rgba(255, 255, 255, 0) calc(50% - 22px)
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.mobile-date-picker-column::after {
|
|
39
|
+
content: '';
|
|
40
|
+
pointer-events: none;
|
|
41
|
+
position: absolute;
|
|
42
|
+
left: 0;
|
|
43
|
+
right: 0;
|
|
44
|
+
top: 50%;
|
|
45
|
+
transform: translateY(-50%);
|
|
46
|
+
height: 44px;
|
|
47
|
+
box-sizing: border-box;
|
|
48
|
+
border-top: 1px solid #e4e4e4;
|
|
49
|
+
border-bottom: 1px solid #e4e4e4;
|
|
50
|
+
z-index: 2;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.mobile-date-picker-wheel {
|
|
54
|
+
--mdp-item-h: 44px;
|
|
55
|
+
height: calc(var(--mdp-item-h) * 5);
|
|
56
|
+
overflow-y: auto;
|
|
57
|
+
touch-action: pan-y;
|
|
58
|
+
-webkit-overflow-scrolling: touch;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.mobile-date-picker-wheel-inner {
|
|
62
|
+
position: relative;
|
|
63
|
+
width: 100%;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.mobile-date-picker-item {
|
|
67
|
+
position: absolute;
|
|
68
|
+
left: 0;
|
|
69
|
+
right: 0;
|
|
70
|
+
height: var(--mdp-item-h);
|
|
71
|
+
line-height: var(--mdp-item-h);
|
|
72
|
+
text-align: center;
|
|
73
|
+
font-size: 16px;
|
|
74
|
+
color: #1b212d;
|
|
75
|
+
}
|
package/demo/src/App.tsx
CHANGED
|
@@ -1,13 +1,36 @@
|
|
|
1
|
+
import { batch, createEffect, omitProps, pickProps, reactive } from '../../src/reactive';
|
|
1
2
|
import { CanlendarPage } from './pages/Canlendar';
|
|
2
3
|
import { ComboBoxPage } from './pages/ComboBox';
|
|
3
4
|
import { DatePickerPage } from './pages/DatePicker';
|
|
4
5
|
import { FormPage } from './pages/Form';
|
|
6
|
+
import { MobileDatePickerPage } from './pages/MobileDatePicker';
|
|
7
|
+
|
|
8
|
+
const Test = (props: { text1: string; text2: string }) => {
|
|
9
|
+
return (
|
|
10
|
+
<div>
|
|
11
|
+
<div>{JSON.stringify({ ...pickProps(props, ['text1']) })}</div>
|
|
12
|
+
<div>{JSON.stringify({ ...omitProps(props, ['text1']) })}</div>
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
5
16
|
|
|
6
17
|
export const App = () => {
|
|
18
|
+
let state = reactive({
|
|
19
|
+
text1: '111',
|
|
20
|
+
text2: '222',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
createEffect(() => {
|
|
24
|
+
console.log(state.text1, state.text2);
|
|
25
|
+
});
|
|
26
|
+
|
|
7
27
|
return (
|
|
8
28
|
<div style={{ 'min-height': '100%' }}>
|
|
29
|
+
<Test {...state}></Test>
|
|
30
|
+
<button onclick={() => (state.text1 = state.text2 = '' + Math.random())}>click</button>
|
|
9
31
|
<CanlendarPage></CanlendarPage>
|
|
10
32
|
<DatePickerPage></DatePickerPage>
|
|
33
|
+
<MobileDatePickerPage></MobileDatePickerPage>
|
|
11
34
|
<ComboBoxPage></ComboBoxPage>
|
|
12
35
|
<FormPage></FormPage>
|
|
13
36
|
</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { createSignal, combineClass, splitProps } from '../reactive';
|
|
2
|
-
|
|
3
1
|
import { JSX } from '../jsx';
|
|
2
|
+
import { createSignal, combineClass, omitProps } from '../reactive';
|
|
4
3
|
import { Canleandar as i18n } from '../i18n';
|
|
5
4
|
import { replaceTemplate } from '../template';
|
|
6
5
|
import { For } from './For';
|
|
@@ -15,7 +14,7 @@ const renderDates = (
|
|
|
15
14
|
className: string,
|
|
16
15
|
todayDate: Date,
|
|
17
16
|
selectedDate?: Date,
|
|
18
|
-
|
|
17
|
+
disableFn?: (year: number, month: number, date: number) => boolean,
|
|
19
18
|
) => {
|
|
20
19
|
let today = todayDate.getFullYear() === year && todayDate.getMonth() === month ? todayDate.getDate() : -1;
|
|
21
20
|
let selected =
|
|
@@ -26,7 +25,7 @@ const renderDates = (
|
|
|
26
25
|
for (let i = from; i <= to; i++) {
|
|
27
26
|
items.push(
|
|
28
27
|
`<span class="canlendar-date${className}${today === i ? ' today' : ''}${selected === i ? ' selected' : ''}${
|
|
29
|
-
|
|
28
|
+
disableFn && disableFn(year, month, i) ? ' disabled' : ''
|
|
30
29
|
}" data-date="${year + '|' + month + '|' + i}">${i}</span>`,
|
|
31
30
|
);
|
|
32
31
|
}
|
|
@@ -40,7 +39,7 @@ const renderPrevMonthItems = (
|
|
|
40
39
|
index: number,
|
|
41
40
|
todayDate: Date,
|
|
42
41
|
selectedDate?: Date,
|
|
43
|
-
|
|
42
|
+
disableFn?: (year: number, month: number, date: number) => boolean,
|
|
44
43
|
) => {
|
|
45
44
|
// 获取上月天数
|
|
46
45
|
let days = new Date(year, month, 0).getDate();
|
|
@@ -52,7 +51,7 @@ const renderPrevMonthItems = (
|
|
|
52
51
|
month = 11;
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
renderDates(items, year, month, days - index + 1, days, ' prev-month', todayDate, selectedDate,
|
|
54
|
+
renderDates(items, year, month, days - index + 1, days, ' prev-month', todayDate, selectedDate, disableFn);
|
|
56
55
|
};
|
|
57
56
|
|
|
58
57
|
const renderNextMonthItems = (
|
|
@@ -62,7 +61,7 @@ const renderNextMonthItems = (
|
|
|
62
61
|
days: number,
|
|
63
62
|
todayDate: Date,
|
|
64
63
|
selectedDate?: Date,
|
|
65
|
-
|
|
64
|
+
disableFn?: (year: number, month: number, date: number) => boolean,
|
|
66
65
|
) => {
|
|
67
66
|
if (month < 11) {
|
|
68
67
|
month++;
|
|
@@ -71,13 +70,13 @@ const renderNextMonthItems = (
|
|
|
71
70
|
month = 0;
|
|
72
71
|
}
|
|
73
72
|
|
|
74
|
-
renderDates(items, year, month, 1, days, ' next-month', todayDate, selectedDate,
|
|
73
|
+
renderDates(items, year, month, 1, days, ' next-month', todayDate, selectedDate, disableFn);
|
|
75
74
|
};
|
|
76
75
|
|
|
77
76
|
const renderItems = (
|
|
78
77
|
showDate: Date,
|
|
79
78
|
selectedDate?: Date,
|
|
80
|
-
|
|
79
|
+
disableFn?: (year: number, month: number, date: number) => boolean,
|
|
81
80
|
) => {
|
|
82
81
|
let today = new Date();
|
|
83
82
|
let year = showDate.getFullYear();
|
|
@@ -92,17 +91,17 @@ const renderItems = (
|
|
|
92
91
|
// 当前月第一天不是周一,渲染上月数据
|
|
93
92
|
if (firstWeek !== 1) {
|
|
94
93
|
index = firstWeek > 0 ? firstWeek - 1 : 6;
|
|
95
|
-
renderPrevMonthItems(items, year, month, index, today, selectedDate,
|
|
94
|
+
renderPrevMonthItems(items, year, month, index, today, selectedDate, disableFn);
|
|
96
95
|
}
|
|
97
96
|
|
|
98
97
|
// 获取当前月的天数
|
|
99
98
|
days = new Date(year, month + 1, 0).getDate();
|
|
100
99
|
// 渲染本月日期
|
|
101
|
-
renderDates(items, year, month, 1, days, '', today, selectedDate,
|
|
100
|
+
renderDates(items, year, month, 1, days, '', today, selectedDate, disableFn);
|
|
102
101
|
|
|
103
102
|
// 当前月最后一天没有占满,渲染下月数据
|
|
104
103
|
if ((index += days) < 42) {
|
|
105
|
-
renderNextMonthItems(items, year, month, 42 - index, today, selectedDate,
|
|
104
|
+
renderNextMonthItems(items, year, month, 42 - index, today, selectedDate, disableFn);
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
return items.join('');
|
|
@@ -138,6 +137,8 @@ const formatMonth = (value: Date) => {
|
|
|
138
137
|
return month > 9 ? month : '0' + month;
|
|
139
138
|
};
|
|
140
139
|
|
|
140
|
+
const OMIT_PROPS = ['class', 'value', 'onValueChange', 'disableFn'] as const;
|
|
141
|
+
|
|
141
142
|
export const Canlendar = (
|
|
142
143
|
props: Omit<JSX.HTMLAttributes<never>, 'children'> & {
|
|
143
144
|
/**
|
|
@@ -151,19 +152,17 @@ export const Canlendar = (
|
|
|
151
152
|
/**
|
|
152
153
|
* 禁用函数
|
|
153
154
|
*/
|
|
154
|
-
|
|
155
|
+
disableFn?: (year: number, month: number, date: number) => boolean;
|
|
155
156
|
},
|
|
156
157
|
) => {
|
|
157
|
-
const [thisProps, restProps] = splitProps(props, ['class', 'value', 'onValueChange', 'disableDate']);
|
|
158
|
-
|
|
159
158
|
let domMonth: HTMLElement;
|
|
160
159
|
let domBody: HTMLElement;
|
|
161
160
|
|
|
162
|
-
const [selectedDate, setSelectedDate] = createSignal(parseDate(
|
|
161
|
+
const [selectedDate, setSelectedDate] = createSignal(parseDate(props.value));
|
|
163
162
|
const [showDate, setShowDate] = createSignal(selectedDate() || new Date());
|
|
164
163
|
|
|
165
164
|
return (
|
|
166
|
-
<div class={combineClass('canlendar',
|
|
165
|
+
<div class={combineClass('canlendar', props.class)} {...omitProps(props, OMIT_PROPS)}>
|
|
167
166
|
<div class="canlendar-header">
|
|
168
167
|
<div ref={domMonth as any} class="canlendar-month">
|
|
169
168
|
{replaceTemplate(i18n.Month, showDate().getFullYear(), formatMonth(showDate()))}
|
|
@@ -185,7 +184,7 @@ export const Canlendar = (
|
|
|
185
184
|
let target = event.target as HTMLElement;
|
|
186
185
|
let date, onValueChange;
|
|
187
186
|
|
|
188
|
-
if (target && (date = target.dataset.date) && (onValueChange =
|
|
187
|
+
if (target && (date = target.dataset.date) && (onValueChange = props.onValueChange)) {
|
|
189
188
|
let dom = domBody.querySelector('.selected') as HTMLElement;
|
|
190
189
|
|
|
191
190
|
if (dom !== target) {
|
|
@@ -197,7 +196,7 @@ export const Canlendar = (
|
|
|
197
196
|
}
|
|
198
197
|
}
|
|
199
198
|
}}
|
|
200
|
-
innerHTML={renderItems(showDate(), selectedDate(),
|
|
199
|
+
innerHTML={renderItems(showDate(), selectedDate(), props.disableFn)}
|
|
201
200
|
></div>
|
|
202
201
|
</div>
|
|
203
202
|
);
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
defineProperty,
|
|
10
10
|
onCleanup,
|
|
11
11
|
onMount,
|
|
12
|
-
|
|
12
|
+
omitProps,
|
|
13
13
|
} from '../reactive';
|
|
14
14
|
|
|
15
15
|
import { For } from './For';
|
|
@@ -48,6 +48,8 @@ if (isBrowser) {
|
|
|
48
48
|
);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
const OMIT_PROPS = ['class', 'each', 'children', 'autoplay', 'interval', 'vertical', 'api'] as const;
|
|
52
|
+
|
|
51
53
|
/**
|
|
52
54
|
* 轮播组件外部调用接口
|
|
53
55
|
*/
|
|
@@ -101,15 +103,6 @@ export const Carousel = <T, U extends JSX.Element>(
|
|
|
101
103
|
api?: (api: CarouselApi) => void;
|
|
102
104
|
},
|
|
103
105
|
) => {
|
|
104
|
-
let [thisProps, restProps] = splitProps(props, [
|
|
105
|
-
'class',
|
|
106
|
-
'each',
|
|
107
|
-
'children',
|
|
108
|
-
'autoplay',
|
|
109
|
-
'interval',
|
|
110
|
-
'vertical',
|
|
111
|
-
'api',
|
|
112
|
-
]);
|
|
113
106
|
let [currentIndex, setCurrentIndex] = createSignal(0);
|
|
114
107
|
|
|
115
108
|
let ref: HTMLElement;
|
|
@@ -121,15 +114,15 @@ export const Carousel = <T, U extends JSX.Element>(
|
|
|
121
114
|
let autoplayTimer: any;
|
|
122
115
|
|
|
123
116
|
// 获取滚动方向
|
|
124
|
-
const scrollType = createMemo(() => (
|
|
125
|
-
const offsetType = createMemo(() => (
|
|
126
|
-
const screenType = createMemo(() => (
|
|
117
|
+
const scrollType = createMemo(() => (props.vertical ? 'scrollTop' : 'scrollLeft'));
|
|
118
|
+
const offsetType = createMemo(() => (props.vertical ? 'offsetTop' : 'offsetLeft'));
|
|
119
|
+
const screenType = createMemo(() => (props.vertical ? 'screenY' : 'screenX'));
|
|
127
120
|
|
|
128
121
|
// 滚动到指定索引
|
|
129
122
|
const scrollTo = (index: number) => {
|
|
130
123
|
let children = ref.children;
|
|
131
124
|
let length = children.length;
|
|
132
|
-
let count =
|
|
125
|
+
let count = props.each.length; // 真实的子项数量
|
|
133
126
|
|
|
134
127
|
if (index < 0) {
|
|
135
128
|
index += count;
|
|
@@ -156,7 +149,7 @@ export const Carousel = <T, U extends JSX.Element>(
|
|
|
156
149
|
|
|
157
150
|
if (index <= 0) {
|
|
158
151
|
// 真实的子项数量
|
|
159
|
-
let count =
|
|
152
|
+
let count = props.each.length;
|
|
160
153
|
|
|
161
154
|
// 先滚动到对应节点的填充节点
|
|
162
155
|
ref[scrollType()] = (ref.children[index + count] as HTMLElement)[offsetType()];
|
|
@@ -219,7 +212,7 @@ export const Carousel = <T, U extends JSX.Element>(
|
|
|
219
212
|
const autoplay = () => {
|
|
220
213
|
clearTimeout(autoplayTimer);
|
|
221
214
|
|
|
222
|
-
if (
|
|
215
|
+
if (props.autoplay !== false) {
|
|
223
216
|
autoplayTimer = setTimeout(
|
|
224
217
|
() => {
|
|
225
218
|
if (!resizing) {
|
|
@@ -233,14 +226,14 @@ export const Carousel = <T, U extends JSX.Element>(
|
|
|
233
226
|
|
|
234
227
|
autoplay();
|
|
235
228
|
},
|
|
236
|
-
|
|
229
|
+
props.interval > 3000 ? props.interval : 3000,
|
|
237
230
|
);
|
|
238
231
|
}
|
|
239
232
|
};
|
|
240
233
|
|
|
241
234
|
// 初始化外部访问接口
|
|
242
|
-
|
|
243
|
-
|
|
235
|
+
props.api &&
|
|
236
|
+
props.api(
|
|
244
237
|
defineProperty(
|
|
245
238
|
{
|
|
246
239
|
backward,
|
|
@@ -278,8 +271,12 @@ export const Carousel = <T, U extends JSX.Element>(
|
|
|
278
271
|
});
|
|
279
272
|
|
|
280
273
|
return (
|
|
281
|
-
<div
|
|
282
|
-
|
|
274
|
+
<div
|
|
275
|
+
ref={ref as any}
|
|
276
|
+
class={combineClass('carousel scrollbar-hidden', props.class)}
|
|
277
|
+
{...omitProps(props, OMIT_PROPS)}
|
|
278
|
+
>
|
|
279
|
+
<For each={fillItems(props.each)}>{props.children}</For>
|
|
283
280
|
</div>
|
|
284
281
|
);
|
|
285
282
|
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { createSignal
|
|
1
|
+
import { createSignal } from 'solid-js';
|
|
2
2
|
|
|
3
3
|
import { JSX } from '../jsx';
|
|
4
|
-
import { combineClass, defineProperty } from '../reactive';
|
|
4
|
+
import { combineClass, defineProperty, omitProps } from '../reactive';
|
|
5
5
|
|
|
6
6
|
const COLLAPSED_CLASS = 'collapsed';
|
|
7
7
|
|
|
8
|
+
const OMIT_PROPS = ['class', 'collapsed', 'collapsedSize', 'getExpandedSize', 'useTransition', 'api'] as const;
|
|
9
|
+
|
|
8
10
|
/**
|
|
9
11
|
* 可收拢面板外部调用接口
|
|
10
12
|
*/
|
|
@@ -52,15 +54,7 @@ export const CollapsiblePanel = (
|
|
|
52
54
|
) => {
|
|
53
55
|
let ref: HTMLElement;
|
|
54
56
|
|
|
55
|
-
const [
|
|
56
|
-
'class',
|
|
57
|
-
'collapsed',
|
|
58
|
-
'collapsedSize',
|
|
59
|
-
'getExpandedSize',
|
|
60
|
-
'useTransition',
|
|
61
|
-
'api',
|
|
62
|
-
]);
|
|
63
|
-
const [collapsed, setCollapsed] = createSignal(thisProps.collapsed || false);
|
|
57
|
+
const [collapsed, setCollapsed] = createSignal(props.collapsed || false);
|
|
64
58
|
|
|
65
59
|
const transitionTo = (size: string, fromSize?: string) => {
|
|
66
60
|
let style = ref.style;
|
|
@@ -85,22 +79,22 @@ export const CollapsiblePanel = (
|
|
|
85
79
|
|
|
86
80
|
// 设置成收拢状态
|
|
87
81
|
if (value) {
|
|
88
|
-
if (
|
|
82
|
+
if (props.useTransition) {
|
|
89
83
|
style.height = ref.offsetHeight + 'px';
|
|
90
84
|
|
|
91
85
|
setTimeout(() => {
|
|
92
|
-
style.height =
|
|
86
|
+
style.height = props.collapsedSize || '';
|
|
93
87
|
classList.add(COLLAPSED_CLASS);
|
|
94
88
|
});
|
|
95
89
|
} else {
|
|
96
|
-
style.height =
|
|
90
|
+
style.height = props.collapsedSize || '';
|
|
97
91
|
classList.add(COLLAPSED_CLASS);
|
|
98
92
|
}
|
|
99
93
|
} else {
|
|
100
|
-
let getExpandedSize =
|
|
94
|
+
let getExpandedSize = props.getExpandedSize;
|
|
101
95
|
|
|
102
96
|
// 设置成展开状态
|
|
103
|
-
if (
|
|
97
|
+
if (props.useTransition) {
|
|
104
98
|
style.height = ref.offsetHeight + 'px';
|
|
105
99
|
|
|
106
100
|
setTimeout(() => {
|
|
@@ -119,8 +113,8 @@ export const CollapsiblePanel = (
|
|
|
119
113
|
}
|
|
120
114
|
};
|
|
121
115
|
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
props.api &&
|
|
117
|
+
props.api(
|
|
124
118
|
defineProperty({ transitionTo }, 'collapsed', {
|
|
125
119
|
get: collapsed,
|
|
126
120
|
set: update,
|
|
@@ -130,8 +124,8 @@ export const CollapsiblePanel = (
|
|
|
130
124
|
return (
|
|
131
125
|
<div
|
|
132
126
|
ref={ref as any}
|
|
133
|
-
class={combineClass('collapsed-panel',
|
|
134
|
-
{...
|
|
127
|
+
class={combineClass('collapsed-panel', props.collapsed && COLLAPSED_CLASS, props.class)}
|
|
128
|
+
{...omitProps(props, OMIT_PROPS)}
|
|
135
129
|
></div>
|
|
136
130
|
);
|
|
137
131
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { JSX } from '../jsx';
|
|
2
|
-
import { combineClass,
|
|
2
|
+
import { combineClass, omitProps } from '../reactive';
|
|
3
3
|
import { disableAutoCloseEvent } from '../dom';
|
|
4
4
|
import { Popup, PopupApi, PopupProps } from './Popup';
|
|
5
5
|
|
|
6
|
+
const OMIT_PROPS = ['class', 'value', 'readonly', 'popupOnFocus', 'popup', 'onPopup', 'api', 'children'] as const;
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* 下拉框组件
|
|
8
10
|
*/
|
|
@@ -27,39 +29,29 @@ export const ComboBox = (
|
|
|
27
29
|
popup?: Omit<JSX.HTMLAttributes<never>, 'children'>;
|
|
28
30
|
},
|
|
29
31
|
) => {
|
|
30
|
-
let [thisProps, restProps] = splitProps(props, [
|
|
31
|
-
'class',
|
|
32
|
-
'value',
|
|
33
|
-
'readonly',
|
|
34
|
-
'popupOnFocus',
|
|
35
|
-
'popup',
|
|
36
|
-
'onPopup',
|
|
37
|
-
'api',
|
|
38
|
-
'children',
|
|
39
|
-
]);
|
|
40
32
|
let popup: PopupApi;
|
|
41
33
|
|
|
42
34
|
const initApi = (api) => {
|
|
43
35
|
popup = api;
|
|
44
|
-
|
|
36
|
+
props.api && props.api(popup);
|
|
45
37
|
};
|
|
46
38
|
|
|
47
39
|
return (
|
|
48
|
-
<div class={combineClass('combobox',
|
|
40
|
+
<div class={combineClass('combobox', props.class)} {...omitProps(props, OMIT_PROPS)}>
|
|
49
41
|
<div class="combobox-host" {...disableAutoCloseEvent}>
|
|
50
42
|
<input
|
|
51
43
|
class="combobox-input"
|
|
52
|
-
value={
|
|
53
|
-
readonly={
|
|
54
|
-
onfocus={() =>
|
|
55
|
-
onclick={() =>
|
|
44
|
+
value={props.value || ''}
|
|
45
|
+
readonly={props.readonly}
|
|
46
|
+
onfocus={() => props.popupOnFocus && popup.openPopup()}
|
|
47
|
+
onclick={() => props.readonly && !props.popupOnFocus && popup.togglePopup()}
|
|
56
48
|
></input>
|
|
57
49
|
<svg class="icon icon-s" aria-hidden={true} onclick={() => popup.togglePopup()}>
|
|
58
50
|
<use href="#icon-dropdown"></use>
|
|
59
51
|
</svg>
|
|
60
52
|
</div>
|
|
61
|
-
<Popup api={initApi} onPopup={
|
|
62
|
-
{
|
|
53
|
+
<Popup api={initApi} onPopup={props.onPopup} {...props.popup}>
|
|
54
|
+
{props.children}
|
|
63
55
|
</Popup>
|
|
64
56
|
</div>
|
|
65
57
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSX } from '../jsx';
|
|
2
|
-
import { combineClass, createSignal,
|
|
2
|
+
import { combineClass, createSignal, omitProps } from '../reactive';
|
|
3
3
|
import { disableAutoCloseEvent } from '../dom';
|
|
4
4
|
import { For } from './For';
|
|
5
5
|
import { Popup, PopupApi } from './Popup';
|
|
@@ -46,6 +46,8 @@ const formatMonth = (value: Date) => {
|
|
|
46
46
|
// );
|
|
47
47
|
// };
|
|
48
48
|
|
|
49
|
+
const OMIT_PROPS = ['class', 'value', 'readonly', 'format', 'children'] as const;
|
|
50
|
+
|
|
49
51
|
/**
|
|
50
52
|
* 日期选择组件
|
|
51
53
|
*/
|
|
@@ -65,26 +67,25 @@ export const DatePicker = (
|
|
|
65
67
|
readonly?: boolean;
|
|
66
68
|
},
|
|
67
69
|
) => {
|
|
68
|
-
let [thisProps, restProps] = splitProps(props, ['class', 'value', 'readonly', 'format', 'children']);
|
|
69
70
|
let popup: PopupApi;
|
|
70
71
|
|
|
71
|
-
const [value, setValue] = createSignal(
|
|
72
|
+
const [value, setValue] = createSignal(props.value && parseDate(props.value));
|
|
72
73
|
|
|
73
74
|
return (
|
|
74
|
-
<div class={combineClass('datepicker',
|
|
75
|
+
<div class={combineClass('datepicker', props.class)} {...omitProps(props, OMIT_PROPS)}>
|
|
75
76
|
<div class="datepicker-host" {...disableAutoCloseEvent}>
|
|
76
77
|
<input
|
|
77
78
|
class="datepicker-input"
|
|
78
|
-
value={formatDate(value(),
|
|
79
|
-
readonly={
|
|
80
|
-
onclick={() =>
|
|
79
|
+
value={formatDate(value(), props.format)}
|
|
80
|
+
readonly={props.readonly}
|
|
81
|
+
onclick={() => props.readonly && popup.togglePopup()}
|
|
81
82
|
></input>
|
|
82
83
|
<svg class="icon icon-s" aria-hidden={true} onclick={() => popup.togglePopup()}>
|
|
83
84
|
<use href="#icon-dropdown"></use>
|
|
84
85
|
</svg>
|
|
85
86
|
</div>
|
|
86
87
|
<Popup api={(api) => (popup = api)} onPopup={() => showPopup()}>
|
|
87
|
-
{
|
|
88
|
+
{props.children}
|
|
88
89
|
</Popup>
|
|
89
90
|
</div>
|
|
90
91
|
);
|