cdui-js 1.0.16 → 1.0.17
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/canlendar.css +19 -8
- package/demo/src/App.tsx +5 -3
- package/package.json +1 -1
- package/src/components/Canlendar.tsx +26 -26
- package/src/components/MonthWidget.tsx +59 -8
- package/src/reactive.ts +41 -14
package/css/canlendar.css
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
.canlendar
|
|
1
|
+
.canlendar,
|
|
2
|
+
.monthwidget {
|
|
2
3
|
max-width: 380px;
|
|
3
4
|
height: 320px;
|
|
4
5
|
border-radius: 12px;
|
|
@@ -8,21 +9,25 @@
|
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
.canlendar-header,
|
|
11
|
-
.canlendar-weeks
|
|
12
|
+
.canlendar-weeks,
|
|
13
|
+
.monthwidget-header {
|
|
12
14
|
display: flex;
|
|
13
15
|
align-items: center;
|
|
14
16
|
height: 36px;
|
|
15
17
|
line-height: 36px;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
.canlendar-
|
|
20
|
+
.canlendar-title,
|
|
21
|
+
.monthwidget-title {
|
|
19
22
|
flex: auto;
|
|
20
23
|
padding-left: 12px;
|
|
21
24
|
font-weight: bold;
|
|
22
25
|
font-size: 14px;
|
|
26
|
+
cursor: pointer;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
.canlendar-header > .icon
|
|
29
|
+
.canlendar-header > .icon,
|
|
30
|
+
.monthwidget-header > .icon {
|
|
26
31
|
box-sizing: border-box;
|
|
27
32
|
width: 40px;
|
|
28
33
|
height: 100%;
|
|
@@ -40,7 +45,8 @@
|
|
|
40
45
|
height: calc(100% - 72px);
|
|
41
46
|
}
|
|
42
47
|
|
|
43
|
-
.canlendar-date
|
|
48
|
+
.canlendar-date,
|
|
49
|
+
.monthwidget-month {
|
|
44
50
|
display: inline-flex;
|
|
45
51
|
justify-content: center;
|
|
46
52
|
align-items: center;
|
|
@@ -49,17 +55,22 @@
|
|
|
49
55
|
cursor: pointer;
|
|
50
56
|
}
|
|
51
57
|
|
|
52
|
-
.canlendar-date.disabled
|
|
58
|
+
.canlendar-date.disabled,
|
|
59
|
+
.monthwidget-month.disabled {
|
|
53
60
|
cursor: not-allowed;
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
.canlendar-date.today,
|
|
57
|
-
.canlendar-date.selected
|
|
64
|
+
.canlendar-date.selected,
|
|
65
|
+
.monthwidget-month.today,
|
|
66
|
+
.monthwidget-month.selected {
|
|
58
67
|
position: relative;
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
.canlendar-date.today::before,
|
|
62
|
-
.canlendar-date.selected::before
|
|
71
|
+
.canlendar-date.selected::before,
|
|
72
|
+
.monthwidget-month.today::before,
|
|
73
|
+
.monthwidget-month.selected::before {
|
|
63
74
|
position: absolute;
|
|
64
75
|
content: '';
|
|
65
76
|
width: 40px;
|
package/demo/src/App.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { batch, createEffect, omitProps, pickProps, reactive } from '../../src/reactive';
|
|
2
|
+
import { Icon } from '../../src/components/Icon';
|
|
2
3
|
import { CanlendarPage } from './pages/Canlendar';
|
|
3
4
|
import { ComboBoxPage } from './pages/ComboBox';
|
|
4
5
|
import { DatePickerPage } from './pages/DatePicker';
|
|
@@ -8,8 +9,8 @@ import { MobileDatePickerPage } from './pages/MobileDatePicker';
|
|
|
8
9
|
const Test = (props: { text1: string; text2: string }) => {
|
|
9
10
|
return (
|
|
10
11
|
<div>
|
|
11
|
-
<div>{
|
|
12
|
-
<div>{
|
|
12
|
+
<div>{pickProps(props, ['text1']).text1}</div>
|
|
13
|
+
<div>{omitProps(props, ['text1']).text2}</div>
|
|
13
14
|
</div>
|
|
14
15
|
);
|
|
15
16
|
};
|
|
@@ -26,8 +27,9 @@ export const App = () => {
|
|
|
26
27
|
|
|
27
28
|
return (
|
|
28
29
|
<div style={{ 'min-height': '100%' }}>
|
|
30
|
+
<Icon name="dropdown" class="test"></Icon>
|
|
29
31
|
<Test {...state}></Test>
|
|
30
|
-
<button onclick={() => (state.text1 = state.text2 = '' + Math.random())}>click</button>
|
|
32
|
+
<button onclick={() => batch(() => (state.text1 = state.text2 = '' + Math.random()))}>click</button>
|
|
31
33
|
<CanlendarPage></CanlendarPage>
|
|
32
34
|
<DatePickerPage></DatePickerPage>
|
|
33
35
|
<MobileDatePickerPage></MobileDatePickerPage>
|
package/package.json
CHANGED
|
@@ -12,14 +12,14 @@ const renderDates = (
|
|
|
12
12
|
from: number,
|
|
13
13
|
to: number,
|
|
14
14
|
className: string,
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
todayValue: Date,
|
|
16
|
+
selectedValue?: Date,
|
|
17
17
|
disableFn?: (year: number, month: number, date: number) => boolean,
|
|
18
18
|
) => {
|
|
19
|
-
let today =
|
|
19
|
+
let today = todayValue.getFullYear() === year && todayValue.getMonth() === month ? todayValue.getDate() : -1;
|
|
20
20
|
let selected =
|
|
21
|
-
|
|
22
|
-
?
|
|
21
|
+
selectedValue && selectedValue.getFullYear() === year && selectedValue.getMonth() === month
|
|
22
|
+
? selectedValue.getDate()
|
|
23
23
|
: -1;
|
|
24
24
|
|
|
25
25
|
for (let i = from; i <= to; i++) {
|
|
@@ -37,8 +37,8 @@ const renderPrevMonthItems = (
|
|
|
37
37
|
year: number,
|
|
38
38
|
month: number,
|
|
39
39
|
index: number,
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
todayValue: Date,
|
|
41
|
+
selectedValue?: Date,
|
|
42
42
|
disableFn?: (year: number, month: number, date: number) => boolean,
|
|
43
43
|
) => {
|
|
44
44
|
// 获取上月天数
|
|
@@ -51,7 +51,7 @@ const renderPrevMonthItems = (
|
|
|
51
51
|
month = 11;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
renderDates(items, year, month, days - index + 1, days, ' prev-month',
|
|
54
|
+
renderDates(items, year, month, days - index + 1, days, ' prev-month', todayValue, selectedValue, disableFn);
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
const renderNextMonthItems = (
|
|
@@ -59,8 +59,8 @@ const renderNextMonthItems = (
|
|
|
59
59
|
year: number,
|
|
60
60
|
month: number,
|
|
61
61
|
days: number,
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
todayValue: Date,
|
|
63
|
+
selectedValue?: Date,
|
|
64
64
|
disableFn?: (year: number, month: number, date: number) => boolean,
|
|
65
65
|
) => {
|
|
66
66
|
if (month < 11) {
|
|
@@ -70,17 +70,17 @@ const renderNextMonthItems = (
|
|
|
70
70
|
month = 0;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
renderDates(items, year, month, 1, days, ' next-month',
|
|
73
|
+
renderDates(items, year, month, 1, days, ' next-month', todayValue, selectedValue, disableFn);
|
|
74
74
|
};
|
|
75
75
|
|
|
76
76
|
const renderItems = (
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
currentValue: Date,
|
|
78
|
+
selectedValue?: Date,
|
|
79
79
|
disableFn?: (year: number, month: number, date: number) => boolean,
|
|
80
80
|
) => {
|
|
81
81
|
let today = new Date();
|
|
82
|
-
let year =
|
|
83
|
-
let month =
|
|
82
|
+
let year = currentValue.getFullYear();
|
|
83
|
+
let month = currentValue.getMonth();
|
|
84
84
|
let firstDate = new Date(year, month, 1); // 获取当前月的第一天
|
|
85
85
|
|
|
86
86
|
let firstWeek = firstDate.getDay();
|
|
@@ -91,17 +91,17 @@ const renderItems = (
|
|
|
91
91
|
// 当前月第一天不是周一,渲染上月数据
|
|
92
92
|
if (firstWeek !== 1) {
|
|
93
93
|
index = firstWeek > 0 ? firstWeek - 1 : 6;
|
|
94
|
-
renderPrevMonthItems(items, year, month, index, today,
|
|
94
|
+
renderPrevMonthItems(items, year, month, index, today, selectedValue, disableFn);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
// 获取当前月的天数
|
|
98
98
|
days = new Date(year, month + 1, 0).getDate();
|
|
99
99
|
// 渲染本月日期
|
|
100
|
-
renderDates(items, year, month, 1, days, '', today,
|
|
100
|
+
renderDates(items, year, month, 1, days, '', today, selectedValue, disableFn);
|
|
101
101
|
|
|
102
102
|
// 当前月最后一天没有占满,渲染下月数据
|
|
103
103
|
if ((index += days) < 42) {
|
|
104
|
-
renderNextMonthItems(items, year, month, 42 - index, today,
|
|
104
|
+
renderNextMonthItems(items, year, month, 42 - index, today, selectedValue, disableFn);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
return items.join('');
|
|
@@ -155,22 +155,22 @@ export const Canlendar = (
|
|
|
155
155
|
disableFn?: (year: number, month: number, date: number) => boolean;
|
|
156
156
|
},
|
|
157
157
|
) => {
|
|
158
|
-
let
|
|
158
|
+
let domTitle: HTMLElement;
|
|
159
159
|
let domBody: HTMLElement;
|
|
160
160
|
|
|
161
|
-
const [
|
|
162
|
-
const [
|
|
161
|
+
const [selectedValue, setSelectedDate] = createSignal(parseDate(props.value));
|
|
162
|
+
const [currentValue, setCurrentValue] = createSignal(selectedValue() || new Date());
|
|
163
163
|
|
|
164
164
|
return (
|
|
165
165
|
<div class={combineClass('canlendar', props.class)} {...omitProps(props, OMIT_PROPS)}>
|
|
166
166
|
<div class="canlendar-header">
|
|
167
|
-
<div ref={
|
|
168
|
-
{replaceTemplate(i18n.Month,
|
|
167
|
+
<div ref={domTitle as any} class="canlendar-title">
|
|
168
|
+
{replaceTemplate(i18n.Month, currentValue().getFullYear(), formatMonth(currentValue()))}
|
|
169
169
|
</div>
|
|
170
|
-
<svg class="icon icon-s" aria-hidden={true} onclick={() =>
|
|
170
|
+
<svg class="icon icon-s" aria-hidden={true} onclick={() => setCurrentValue(switchMonth(currentValue(), -1))}>
|
|
171
171
|
<use href="#icon-backward"></use>
|
|
172
172
|
</svg>
|
|
173
|
-
<svg class="icon icon-s" aria-hidden={true} onclick={() =>
|
|
173
|
+
<svg class="icon icon-s" aria-hidden={true} onclick={() => setCurrentValue(switchMonth(currentValue(), 1))}>
|
|
174
174
|
<use href="#icon-forward"></use>
|
|
175
175
|
</svg>
|
|
176
176
|
</div>
|
|
@@ -196,7 +196,7 @@ export const Canlendar = (
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
}}
|
|
199
|
-
innerHTML={renderItems(
|
|
199
|
+
innerHTML={renderItems(currentValue(), selectedValue(), props.disableFn)}
|
|
200
200
|
></div>
|
|
201
201
|
</div>
|
|
202
202
|
);
|
|
@@ -5,12 +5,38 @@ import { Canleandar as i18n } from '../i18n';
|
|
|
5
5
|
import { replaceTemplate } from '../template';
|
|
6
6
|
import { For } from './For';
|
|
7
7
|
|
|
8
|
+
const formatMonth = (month: number) => {
|
|
9
|
+
return month > 9 ? month : '0' + month;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const getCurrentMonth = () => {
|
|
13
|
+
let date = new Date();
|
|
14
|
+
return [date.getFullYear(), date.getMonth() + 1];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const switchMonth = (value: [year: number, month: number], offset: 1 | -1) => {
|
|
18
|
+
let year = value[0];
|
|
19
|
+
let month = value[1] + offset;
|
|
20
|
+
|
|
21
|
+
if (month === 0) {
|
|
22
|
+
month = 12;
|
|
23
|
+
year--;
|
|
24
|
+
} else if (month > 12) {
|
|
25
|
+
month = 1;
|
|
26
|
+
year++;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return [year, month];
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const MONTH_LIST = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
|
|
33
|
+
|
|
8
34
|
export const MonthWidget = (
|
|
9
35
|
props: Omit<JSX.HTMLAttributes<never>, 'children'> & {
|
|
10
36
|
/**
|
|
11
|
-
*
|
|
37
|
+
* 年月值
|
|
12
38
|
*/
|
|
13
|
-
value?:
|
|
39
|
+
value?: [year: number, month: number];
|
|
14
40
|
/**
|
|
15
41
|
* 值变更事件
|
|
16
42
|
*/
|
|
@@ -21,21 +47,46 @@ export const MonthWidget = (
|
|
|
21
47
|
disableFn?: (year: number, month: number, date: number) => boolean;
|
|
22
48
|
},
|
|
23
49
|
) => {
|
|
50
|
+
let domTitle: HTMLElement;
|
|
24
51
|
let domBody: HTMLElement;
|
|
25
52
|
|
|
53
|
+
const [selectedValue, setSelectedDate] = createSignal(props.value);
|
|
54
|
+
const [currentValue, setCurrentValue] = createSignal(selectedValue() || getCurrentMonth());
|
|
55
|
+
|
|
26
56
|
return (
|
|
27
57
|
<div>
|
|
28
|
-
|
|
29
|
-
<div ref={
|
|
30
|
-
{replaceTemplate(i18n.Month,
|
|
58
|
+
<div class="monthwidget-header">
|
|
59
|
+
<div ref={domTitle as any} class="monthwidget-title">
|
|
60
|
+
{replaceTemplate(i18n.Month, currentValue()[0], formatMonth(currentValue()[1]))}
|
|
31
61
|
</div>
|
|
32
|
-
<svg class="icon icon-s" aria-hidden={true} onclick={() =>
|
|
62
|
+
<svg class="icon icon-s" aria-hidden={true} onclick={() => setCurrentValue(switchMonth(selectedValue(), -1))}>
|
|
33
63
|
<use href="#icon-backward"></use>
|
|
34
64
|
</svg>
|
|
35
|
-
<svg class="icon icon-s" aria-hidden={true} onclick={() =>
|
|
65
|
+
<svg class="icon icon-s" aria-hidden={true} onclick={() => setCurrentValue(switchMonth(selectedValue(), 1))}>
|
|
36
66
|
<use href="#icon-forward"></use>
|
|
37
67
|
</svg>
|
|
38
|
-
</div>
|
|
68
|
+
</div>
|
|
69
|
+
<div
|
|
70
|
+
ref={domBody as any}
|
|
71
|
+
class="monthwidget-body"
|
|
72
|
+
onclick={(event) => {
|
|
73
|
+
let target = event.target as HTMLElement;
|
|
74
|
+
let value, onValueChange;
|
|
75
|
+
|
|
76
|
+
if (target && (onValueChange = props.onValueChange)) {
|
|
77
|
+
let dom = domBody.querySelector('.selected') as HTMLElement;
|
|
78
|
+
|
|
79
|
+
if (dom !== target) {
|
|
80
|
+
value = [currentValue()[0], +target.textContent];
|
|
81
|
+
|
|
82
|
+
setSelectedDate(value);
|
|
83
|
+
onValueChange(value);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
<For each={MONTH_LIST}>{(item) => <span>{item}</span>}</For>
|
|
89
|
+
</div>
|
|
39
90
|
</div>
|
|
40
91
|
);
|
|
41
92
|
};
|
package/src/reactive.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, createEffect, createSignal, createUniqueId, lazy } from 'solid-js';
|
|
1
|
+
import { Component, createEffect, createSignal, createUniqueId, lazy, splitProps } from 'solid-js';
|
|
2
2
|
|
|
3
3
|
const getOwnPropertyNames = Object.getOwnPropertyNames;
|
|
4
4
|
|
|
@@ -91,31 +91,58 @@ export interface SSRRenderPage {
|
|
|
91
91
|
* @param pickPropertyNames 要选择的组件属性名集合
|
|
92
92
|
*/
|
|
93
93
|
export const pickProps = <T extends Record<any, any>, K extends readonly (keyof T)[]>(
|
|
94
|
-
|
|
94
|
+
props: T,
|
|
95
95
|
pickPropertyNames: K,
|
|
96
96
|
): Pick<T, K[number]> => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
let result = {};
|
|
98
|
+
|
|
99
|
+
for (let i = 0, l = pickPropertyNames.length; i < l; i++) {
|
|
100
|
+
let name = pickPropertyNames[i];
|
|
101
|
+
|
|
102
|
+
defineProperty(result, name, {
|
|
103
|
+
get: () => props[name],
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return result as Pick<T, K[number]>;
|
|
108
|
+
// return new Proxy(props, {
|
|
109
|
+
// ownKeys(target) {
|
|
110
|
+
// return getOwnPropertyNames(target).filter((key) => pickPropertyNames.indexOf(key) >= 0);
|
|
111
|
+
// },
|
|
112
|
+
// });
|
|
102
113
|
};
|
|
103
114
|
|
|
104
115
|
/**
|
|
105
116
|
* 排除部分组件属性
|
|
106
117
|
*
|
|
107
|
-
* @param
|
|
118
|
+
* @param props 组件属性集合
|
|
108
119
|
* @param omitPropertyNames 要排除的组件属性名集合
|
|
109
120
|
*/
|
|
110
121
|
export const omitProps = <T extends Record<any, any>, K extends readonly (keyof T)[]>(
|
|
111
|
-
|
|
122
|
+
props: T,
|
|
112
123
|
omitPropertyNames: K,
|
|
113
124
|
): Omit<T, K[number]> => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
125
|
+
let names = getOwnPropertyNames(props);
|
|
126
|
+
let result = {};
|
|
127
|
+
|
|
128
|
+
for (let i = 0, l = names.length; i < l; i++) {
|
|
129
|
+
let name = names[i];
|
|
130
|
+
|
|
131
|
+
if (omitPropertyNames.indexOf(name) < 0) {
|
|
132
|
+
defineProperty(result, name, {
|
|
133
|
+
get: () => props[name],
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return result as Omit<T, K[number]>;
|
|
139
|
+
|
|
140
|
+
// return splitProps(props, omitPropertyNames)[1] as any;
|
|
141
|
+
// return new Proxy(props, {
|
|
142
|
+
// ownKeys(target) {
|
|
143
|
+
// return getOwnPropertyNames(target).filter((key) => omitPropertyNames.indexOf(key) < 0);
|
|
144
|
+
// },
|
|
145
|
+
// }) as unknown as Omit<T, K[number]>;
|
|
119
146
|
};
|
|
120
147
|
|
|
121
148
|
/**
|