cdui-js 1.0.22 → 1.0.23

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.
@@ -1,4 +1,5 @@
1
1
  .datewidget {
2
+ position: relative;
2
3
  max-width: 380px;
3
4
  height: 320px;
4
5
  border-radius: 12px;
@@ -7,6 +8,11 @@
7
8
  font-size: 16px;
8
9
  }
9
10
 
11
+ .datewidget:has(.datewidget) > .datewidget-header,
12
+ .datewidget:has(.datewidget) > .datewidget-body {
13
+ display: none;
14
+ }
15
+
10
16
  .datewidget-header {
11
17
  display: flex;
12
18
  align-items: center;
@@ -66,6 +72,8 @@
66
72
  flex: auto;
67
73
  text-align: center;
68
74
  vertical-align: middle;
75
+ font-weight: 400;
76
+ font-size: 10px;
69
77
  }
70
78
 
71
79
  .canlendar-body {
package/demo/src/App.tsx CHANGED
@@ -3,7 +3,6 @@ import { CarouselPage } from './pages/Carousel';
3
3
  import { ComboBoxPage } from './pages/ComboBox';
4
4
  import { DatePickerPage } from './pages/DatePicker';
5
5
  import { FormPage } from './pages/Form';
6
- import { MobileDatePickerPage } from './pages/MobileDatePicker';
7
6
 
8
7
  export const App = () => {
9
8
  return (
@@ -11,7 +10,6 @@ export const App = () => {
11
10
  <CarouselPage></CarouselPage>
12
11
  <CanlendarPage></CanlendarPage>
13
12
  <DatePickerPage></DatePickerPage>
14
- <MobileDatePickerPage></MobileDatePickerPage>
15
13
  <ComboBoxPage></ComboBoxPage>
16
14
  <FormPage></FormPage>
17
15
  </div>
@@ -7,12 +7,12 @@ export const CanlendarPage = () => {
7
7
  <div>
8
8
  <Canlendar
9
9
  value={new Date()}
10
- onValueChange={(date) => {
11
- console.log(date);
10
+ onchange={(event) => {
11
+ console.log(event.detail);
12
12
  }}
13
13
  ></Canlendar>
14
- <MonthWidget></MonthWidget>
15
- <YearWidget></YearWidget>
14
+ {/* <MonthWidget></MonthWidget>
15
+ <YearWidget></YearWidget> */}
16
16
  </div>
17
17
  );
18
18
  };
@@ -49,11 +49,18 @@ export const FormPage = () => {
49
49
  ],
50
50
  a: 1,
51
51
  b: 2,
52
+
53
+ form: {
54
+ username: ''
55
+ }
52
56
  });
53
57
 
54
58
  let form: FormApi;
55
59
 
60
+ let form2: FormApi
61
+
56
62
  return (
63
+ <>
57
64
  <Form data={state} rules={{}} align={state.align} labelWidth={state.labelWidth} api={(api) => (form = api)}>
58
65
  <For each={state.items}>
59
66
  {(item) => (
@@ -92,5 +99,17 @@ export const FormPage = () => {
92
99
  clearErrors
93
100
  </button>
94
101
  </Form>
102
+
103
+ <Form data={state.form} rules={{}} api={(api) => (form2 = api)}>
104
+ <FormItem label="username" required field='username'>
105
+ <input value={state.form.username} onchange={e => state.form.username = e.target.value} />
106
+ </FormItem>
107
+ </Form>
108
+
109
+ <button onclick={async() => {
110
+ const valid = await form2.validate()
111
+ console.log(valid)
112
+ }}>click</button>
113
+ </>
95
114
  );
96
115
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdui-js",
3
- "version": "1.0.22",
3
+ "version": "1.0.23",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "bin": {
@@ -1,8 +1,29 @@
1
1
  import { JSX } from '../jsx';
2
- import { createSignal, combineClass, omitProps } from '../reactive';
3
- import { Canleandar as i18n } from '../i18n';
2
+ import { createSignal, combineClass, omitProps, render } from '../reactive';
3
+ import { Canlendar as i18n } from '../i18n';
4
4
  import { replaceTemplate } from '../template';
5
+
5
6
  import { For } from './For';
7
+ import { MonthWidget } from './MonthWidget';
8
+
9
+ export const parseDate = (value: Date | string | number) => {
10
+ if (value) {
11
+ switch (typeof value) {
12
+ case 'number':
13
+ return new Date(value);
14
+
15
+ case 'string':
16
+ return new Date(value.replace(/\//g, '-'));
17
+
18
+ default:
19
+ return value;
20
+ }
21
+ }
22
+ };
23
+
24
+ export const getYearMonth = (date: Date) => {
25
+ return [date.getFullYear(), date.getMonth() + 1] as const;
26
+ };
6
27
 
7
28
  // 渲染日期项
8
29
  const renderDates = (
@@ -16,9 +37,9 @@ const renderDates = (
16
37
  selectedValue?: Date,
17
38
  disableFn?: (year: number, month: number, date: number) => boolean,
18
39
  ) => {
19
- let today = todayValue.getFullYear() === year && todayValue.getMonth() === month ? todayValue.getDate() : -1;
40
+ let today = todayValue.getFullYear() === year && todayValue.getMonth() + 1 === month ? todayValue.getDate() : -1;
20
41
  let selected =
21
- selectedValue && selectedValue.getFullYear() === year && selectedValue.getMonth() === month
42
+ selectedValue && selectedValue.getFullYear() === year && selectedValue.getMonth() + 1 === month
22
43
  ? selectedValue.getDate()
23
44
  : -1;
24
45
 
@@ -26,7 +47,7 @@ const renderDates = (
26
47
  items.push(
27
48
  `<span class="datewidget-item${className}${today === i ? ' today' : ''}${selected === i ? ' selected' : ''}${
28
49
  disableFn && disableFn(year, month, i) ? ' disabled' : ''
29
- }" data-date="${year + '|' + month + '|' + i}">${i}</span>`,
50
+ }" data-day="${year + '|' + month + '|' + i}">${i}</span>`,
30
51
  );
31
52
  }
32
53
  };
@@ -74,14 +95,14 @@ const renderNextMonthItems = (
74
95
  };
75
96
 
76
97
  const renderItems = (
77
- currentValue: Date,
98
+ currentValue: readonly [year: number, month: number],
78
99
  selectedValue?: Date,
79
100
  disableFn?: (year: number, month: number, date: number) => boolean,
80
101
  ) => {
81
102
  let today = new Date();
82
- let year = currentValue.getFullYear();
83
- let month = currentValue.getMonth();
84
- let firstDate = new Date(year, month, 1); // 获取当前月的第一天
103
+ let year = currentValue[0];
104
+ let month = currentValue[1];
105
+ let firstDate = new Date(year, month - 1, 1); // 获取当前月的第一天
85
106
 
86
107
  let firstWeek = firstDate.getDay();
87
108
  let items = [];
@@ -95,55 +116,69 @@ const renderItems = (
95
116
  }
96
117
 
97
118
  // 获取当前月的天数
98
- days = new Date(year, month + 1, 0).getDate();
119
+ days = new Date(year, month, 0).getDate();
120
+
99
121
  // 渲染本月日期
100
122
  renderDates(items, year, month, 1, days, '', today, selectedValue, disableFn);
101
123
 
102
124
  // 当前月最后一天没有占满,渲染下月数据
103
125
  if ((index += days) < 42) {
104
- renderNextMonthItems(items, year, month, 42 - index, today, selectedValue, disableFn);
126
+ renderNextMonthItems(items, year, month + 1, 42 - index, today, selectedValue, disableFn);
105
127
  }
106
128
 
107
129
  return items.join('');
108
130
  };
109
131
 
110
- const switchMonth = (value: Date, offset: 1 | -1) => {
111
- let date = new Date(value.getTime());
112
- let month = value.getMonth();
132
+ const switchMonth = (value: readonly [year: number, month: number], offset: 1 | -1) => {
133
+ let year = value[0];
134
+ let month = value[1] + offset;
113
135
 
114
- date.setMonth(month + offset);
136
+ if (month > 12) {
137
+ year++;
138
+ month = 1;
139
+ } else if (!month) {
140
+ year--;
141
+ month = 12;
142
+ }
115
143
 
116
- return date;
144
+ return [year, month] as const;
117
145
  };
118
146
 
119
- export const parseDate = (value: Date | string | number) => {
120
- if (value) {
121
- switch (typeof value) {
122
- case 'number':
123
- return new Date(value);
147
+ const formatMonth = (month: number) => {
148
+ return month > 9 ? month : '0' + month;
149
+ };
124
150
 
125
- case 'string':
126
- return new Date(value.replace(/\//g, '-'));
151
+ const showMonthWidget = (dom: HTMLElement, setCurrentValue: (value) => void, selectedValue?: Date) => {
152
+ let value =
153
+ selectedValue && ([selectedValue.getFullYear(), selectedValue.getMonth() + 1] as [year: number, month: number]);
127
154
 
128
- default:
129
- return value;
130
- }
131
- }
132
- };
155
+ render(
156
+ () => (
157
+ <MonthWidget
158
+ value={value}
159
+ style={{ position: 'absolute', top: '0', left: '0', width: '100%', border: 'none' }}
160
+ onchange={(event) => {
161
+ let result = event.detail;
133
162
 
134
- const formatMonth = (value: Date) => {
135
- let month = value.getMonth() + 1;
163
+ dom.removeChild(event.target as HTMLElement);
136
164
 
137
- return month > 9 ? month : '0' + month;
165
+ if (!selectedValue || result[0] !== value[0] || result[1] !== value[1]) {
166
+ setCurrentValue(result);
167
+ }
168
+ }}
169
+ ></MonthWidget>
170
+ ),
171
+ dom,
172
+ );
138
173
  };
139
174
 
140
- const OMIT_PROPS = ['class', 'value', 'onValueChange', 'disableFn'] as const;
175
+ const OMIT_PROPS = ['class', 'value', 'disableFn'] as const;
141
176
 
142
177
  /**
143
178
  * 日历组件
144
179
  */
145
180
  export const Canlendar = (
146
- props: Omit<JSX.HTMLAttributes<never>, 'children'> & {
181
+ props: Omit<JSX.HTMLAttributes<never>, 'children' | 'onchange'> & {
147
182
  /**
148
183
  * 日期值
149
184
  */
@@ -151,24 +186,27 @@ export const Canlendar = (
151
186
  /**
152
187
  * 值变更事件
153
188
  */
154
- onValueChange?: (value: Date) => void;
189
+ onchange?: (event: CustomEvent<Date>) => void;
155
190
  /**
156
191
  * 禁用函数
157
192
  */
158
193
  disableFn?: (year: number, month: number, date: number) => boolean;
159
194
  },
160
195
  ) => {
161
- let domTitle: HTMLElement;
162
- let domBody: HTMLElement;
196
+ let dom: HTMLElement;
163
197
 
164
198
  const [selectedValue, setSelectedValue] = createSignal(parseDate(props.value));
165
- const [currentValue, setCurrentValue] = createSignal(selectedValue() || new Date());
199
+ const [currentValue, setCurrentValue] = createSignal(getYearMonth(selectedValue() || new Date()));
166
200
 
167
201
  return (
168
- <div class={combineClass('canlendar datewidget', props.class)} {...omitProps(props, OMIT_PROPS)}>
202
+ <div
203
+ ref={dom as any}
204
+ class={combineClass('canlendar datewidget', props.class)}
205
+ {...(omitProps(props, OMIT_PROPS) as any)}
206
+ >
169
207
  <div class="datewidget-header canlendar-header">
170
- <div ref={domTitle as any} class="datewidget-title">
171
- {replaceTemplate(i18n.Title, currentValue().getFullYear(), formatMonth(currentValue()))}
208
+ <div class="datewidget-title" onclick={() => showMonthWidget(dom, setCurrentValue, selectedValue())}>
209
+ {replaceTemplate(i18n.Title, currentValue()[0], formatMonth(currentValue()[1]))}
172
210
  </div>
173
211
  <svg class="icon icon-s" aria-hidden={true} onclick={() => setCurrentValue(switchMonth(currentValue(), -1))}>
174
212
  <use href="#icon-backward"></use>
@@ -181,21 +219,26 @@ export const Canlendar = (
181
219
  <For each={i18n.Weeks}>{(item) => <span>{item}</span>}</For>
182
220
  </div>
183
221
  <div
184
- ref={domBody as any}
185
222
  class="datewidget-body canlendar-body"
186
223
  onclick={(event) => {
187
224
  let target = event.target as HTMLElement;
188
- let date;
225
+ let day;
189
226
 
190
- while (target && target !== domBody) {
191
- if ((date = target.dataset.date)) {
227
+ while (target && target !== dom) {
228
+ if ((day = target.dataset.day)) {
192
229
  // 没有选中
193
230
  if (!target.classList.contains('selected')) {
194
- date = date.split('|');
195
- date = new Date(date[0] | 0, date[1] | 0, date[2] | 0);
231
+ day = day.split('|');
232
+ day = new Date(day[0] | 0, (day[1] | 0) - 1, day[2] | 0);
233
+
234
+ setSelectedValue(day);
196
235
 
197
- setSelectedValue(date);
198
- props.onValueChange && props.onValueChange(date);
236
+ dom.dispatchEvent(
237
+ new CustomEvent('change', {
238
+ detail: day,
239
+ bubbles: true, // 允许事件冒泡
240
+ }),
241
+ );
199
242
  }
200
243
 
201
244
  break;
@@ -38,11 +38,6 @@ export const ComboBox = (
38
38
  ) => {
39
39
  let popup: PopupApi;
40
40
 
41
- const initApi = (api) => {
42
- popup = api;
43
- props.api && props.api(popup);
44
- };
45
-
46
41
  const formItem = useContext(FormItemContext);
47
42
 
48
43
  const initFormItem = (dom: HTMLInputElement) => {
@@ -69,7 +64,14 @@ export const ComboBox = (
69
64
  <use href="#icon-dropdown"></use>
70
65
  </svg>
71
66
  </div>
72
- <Popup api={initApi} onPopup={props.onPopup} {...props.popup}>
67
+ <Popup
68
+ api={(api) => {
69
+ popup = api;
70
+ props.api && props.api(popup);
71
+ }}
72
+ onPopup={props.onPopup}
73
+ {...props.popup}
74
+ >
73
75
  {props.children}
74
76
  </Popup>
75
77
  </div>
@@ -1,58 +1,38 @@
1
1
  import { JSX } from '../jsx';
2
- import { combineClass, createSignal, omitProps } from '../reactive';
2
+ import { combineClass, createSignal, omitProps, render } from '../reactive';
3
3
  import { disableAutoCloseEvent } from '../dom';
4
- import { For } from './For';
5
4
  import { Popup, PopupApi } from './Popup';
6
- import { parseDate } from './Canlendar';
5
+ import { Canlendar, parseDate } from './Canlendar';
7
6
 
8
7
  const formatDate = (date: Date, format: string) => {
9
8
  return date ? date.toLocaleString() : '';
10
9
  };
11
10
 
12
- const showPopup = () => {};
11
+ const showCanlendar = (dom: HTMLElement) => {
12
+ let popup: PopupApi;
13
13
 
14
- const MobileTouchScroll = (props: { items: (number | string)[] }) => {
15
- return (
16
- <div>
17
- <For each={props.items}>{(item) => <div>{item}</div>}</For>
18
- </div>
14
+ render(
15
+ () => (
16
+ <Popup
17
+ api={(api) => {
18
+ popup = api;
19
+ api.openPopup();
20
+ }}
21
+ >
22
+ <Canlendar></Canlendar>
23
+ </Popup>
24
+ ),
25
+ dom,
19
26
  );
20
27
  };
21
28
 
22
- const formatMonth = (value: Date) => {
23
- let month = value.getMonth() + 1;
24
-
25
- return month > 9 ? month : '0' + month;
26
- };
27
-
28
- // const computeMobileList = (value: Date) => {
29
- // let year = value.getFullYear();
30
- // let month = value.getMonth() + 1;
31
- // let date = value.getDate();
32
-
33
- // return [
34
- // year > 5 ? [year - 2, year - 1, year, year + 1, year + 2]: [1,2,3,4,5],
35
- // [formatMonth(month - 2), ]
36
- // ];
37
- // };
38
-
39
- // const MobileDatePicker = (yearList: number[], monthList: string[], dateList: string[]) => {
40
- // return (
41
- // <div>
42
- // <MobileTouchScroll items={yearList}></MobileTouchScroll>
43
- // <MobileTouchScroll items={monthList}></MobileTouchScroll>
44
- // <MobileTouchScroll items={dateList}></MobileTouchScroll>
45
- // </div>
46
- // );
47
- // };
48
-
49
- const OMIT_PROPS = ['class', 'value', 'readonly', 'format', 'children'] as const;
29
+ const OMIT_PROPS = ['class', 'value', 'readonly', 'format'] as const;
50
30
 
51
31
  /**
52
32
  * 日期选择组件
53
33
  */
54
34
  export const DatePicker = (
55
- props?: JSX.HTMLAttributes<never> & {
35
+ props?: Omit<JSX.HTMLAttributes<never>, 'children' | 'onchange'> & {
56
36
  /**
57
37
  * 值
58
38
  */
@@ -65,28 +45,33 @@ export const DatePicker = (
65
45
  * 是否只读
66
46
  */
67
47
  readonly?: boolean;
48
+ /**
49
+ * 值变更事件
50
+ */
51
+ onchange?: (event: CustomEvent<Date>) => void;
52
+ /**
53
+ * 禁用函数
54
+ */
55
+ disableFn?: (year: number, month: number, date: number) => boolean;
68
56
  },
69
57
  ) => {
70
- let popup: PopupApi;
58
+ let dom: HTMLElement;
71
59
 
72
60
  const [value, setValue] = createSignal(props.value && parseDate(props.value));
73
61
 
74
62
  return (
75
- <div class={combineClass('datepicker', props.class)} {...omitProps(props, OMIT_PROPS)}>
63
+ <div ref={dom as any} class={combineClass('datepicker', props.class)} {...(omitProps(props, OMIT_PROPS) as any)}>
76
64
  <div class="datepicker-host" {...disableAutoCloseEvent}>
77
65
  <input
78
66
  class="datepicker-input"
79
67
  value={formatDate(value(), props.format)}
80
68
  readonly={props.readonly}
81
- onclick={() => props.readonly && popup.togglePopup()}
69
+ onclick={() => props.readonly && showCanlendar(dom)}
82
70
  ></input>
83
- <svg class="icon icon-s" aria-hidden={true} onclick={() => popup.togglePopup()}>
71
+ <svg class="icon icon-s" aria-hidden={true} onclick={() => showCanlendar(dom)}>
84
72
  <use href="#icon-dropdown"></use>
85
73
  </svg>
86
74
  </div>
87
- <Popup api={(api) => (popup = api)} onPopup={() => showPopup()}>
88
- {props.children}
89
- </Popup>
90
75
  </div>
91
76
  );
92
77
  };
@@ -466,6 +466,7 @@ const validate = async (
466
466
 
467
467
  if (error) {
468
468
  showError(child, error);
469
+ result = false;
469
470
  } else {
470
471
  removeError(child);
471
472
  }
@@ -11,7 +11,7 @@ export const MonthPicker = (
11
11
  /**
12
12
  * 值变更事件
13
13
  */
14
- onValueChange?: (value: Date) => void;
14
+ onchange?: (event: CustomEvent) => void;
15
15
  /**
16
16
  * 禁用函数
17
17
  */