cdui-js 1.0.13 → 1.0.14

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/build/css.ts +9 -18
  2. package/css/all.css +8 -3
  3. package/css/base.css +4 -0
  4. package/css/button.css +3 -0
  5. package/css/canlendar.css +1 -26
  6. package/css/carousel.css +0 -3
  7. package/css/combobox.css +5 -25
  8. package/css/datepicker.css +38 -0
  9. package/css/form.css +19 -0
  10. package/css/popup.css +33 -0
  11. package/css/textbox.css +5 -0
  12. package/{css → demo/css}/build.ts +1 -1
  13. package/{css → demo/css}/css.md +45 -4
  14. package/demo/icons/build.ts +17 -0
  15. package/{index.html → demo/index.html} +20 -2
  16. package/demo/src/App.tsx +15 -0
  17. package/demo/src/css/all.css +3 -0
  18. package/demo/src/css/atomic.css +935 -0
  19. package/demo/src/main.ts +10 -0
  20. package/demo/{pages → src/pages}/Canlendar.tsx +1 -1
  21. package/demo/src/pages/ComboBox.tsx +10 -0
  22. package/demo/src/pages/DatePicker.tsx +5 -0
  23. package/demo/src/pages/Form.tsx +66 -0
  24. package/{vite.config.ts → demo/vite.config.ts} +2 -2
  25. package/icons/backward.svg +2 -2
  26. package/icons/close.svg +3 -3
  27. package/icons/dropdown.svg +2 -2
  28. package/icons/forward.svg +2 -2
  29. package/package.json +4 -2
  30. package/src/components/Button.tsx +5 -0
  31. package/src/components/Canlendar.tsx +23 -45
  32. package/src/components/Carousel.tsx +13 -4
  33. package/src/components/CollapsiblePanel.tsx +2 -4
  34. package/src/components/ComboBox.tsx +50 -146
  35. package/src/components/DatePicker.tsx +91 -0
  36. package/src/components/Dialog.ts +1 -1
  37. package/src/components/Form.tsx +141 -418
  38. package/src/components/Icon.tsx +2 -1
  39. package/src/components/Popup.tsx +220 -0
  40. package/src/components/Pulldown.tsx +2 -3
  41. package/src/components/TextBox.tsx +8 -0
  42. package/src/dom.ts +55 -39
  43. package/src/http.ts +23 -19
  44. package/src/i18n/languages/en.json +3 -3
  45. package/src/index.ts +5 -1
  46. package/src/reactive.ts +9 -0
  47. package/demo/App.tsx +0 -31
  48. package/demo/main.ts +0 -7
  49. package/demo/pages/ComboBox.tsx +0 -10
  50. package/demo/pages/Form.tsx +0 -32
  51. package/icons/build.ts +0 -24
  52. /package/demo/{test.js → src/test.js} +0 -0
@@ -0,0 +1,10 @@
1
+ import './css/all.css';
2
+
3
+ import { startAutoCloseEvent, render } from '../../src';
4
+ import { App } from './App';
5
+
6
+ // 客户端渲染
7
+ render(App, document.body);
8
+
9
+ // 开始侦听自动关闭事件
10
+ startAutoCloseEvent();
@@ -1,4 +1,4 @@
1
- import { Canlendar } from '../../src/components/Canlendar';
1
+ import { Canlendar } from '../../../src/components/Canlendar';
2
2
 
3
3
  export const CanlendarPage = () => {
4
4
  return (
@@ -0,0 +1,10 @@
1
+ import { ComboBox } from '../../../src/components/ComboBox';
2
+
3
+ export const ComboBoxPage = () => {
4
+ return (
5
+ <ComboBox popup={{ style: { width: '200px', padding: '8px 0' } }}>
6
+ <div>111</div>
7
+ <div>222</div>
8
+ </ComboBox>
9
+ );
10
+ };
@@ -0,0 +1,5 @@
1
+ import { DatePicker } from '../../../src/components/DatePicker';
2
+
3
+ export const DatePickerPage = () => {
4
+ return <DatePicker></DatePicker>;
5
+ };
@@ -0,0 +1,66 @@
1
+ import { reactive } from '../../../src/reactive';
2
+ import { For } from '../../../src/components/For';
3
+ import { TextBox } from '../../../src/components/TextBox';
4
+ import { Form, FormApi, FormItem, ValidateRules } from '../../../src/components/Form';
5
+
6
+ const rules: ValidateRules = {
7
+ a: {
8
+ type: 'number',
9
+ },
10
+ };
11
+
12
+ export const FormPage = () => {
13
+ const state = reactive({
14
+ align: 'left' as 'top' | 'left',
15
+ labelWidth: '60px',
16
+ items: [
17
+ {
18
+ field: 'a',
19
+ label: '111',
20
+ required: true,
21
+ },
22
+ {
23
+ field: 'b',
24
+ label: '222',
25
+ required: true,
26
+ labelWidth: '100px',
27
+ },
28
+ ],
29
+ });
30
+
31
+ let form: FormApi;
32
+
33
+ return (
34
+ <Form data={{}} rules={{}} align={state.align} labelWidth={state.labelWidth} api={(api) => (form = api)}>
35
+ <For each={state.items}>
36
+ {(item) => (
37
+ <FormItem field={item.field} label={item.label} required={item.required}>
38
+ <TextBox></TextBox>
39
+ </FormItem>
40
+ )}
41
+ </For>
42
+ <button
43
+ type="button"
44
+ onclick={() =>
45
+ state.items.push({
46
+ field: '?',
47
+ label: '???',
48
+ required: false,
49
+ labelWidth: '100px',
50
+ })
51
+ }
52
+ >
53
+ append
54
+ </button>
55
+ <button type="button" onclick={() => (state.align = state.align === 'left' ? 'top' : 'left')}>
56
+ align
57
+ </button>
58
+ <button type="button" onclick={() => (state.labelWidth = parseInt(state.labelWidth) + 10 + 'px')}>
59
+ labelWidth
60
+ </button>
61
+ <button type="button" onclick={() => form.validate()}>
62
+ validate
63
+ </button>
64
+ </Form>
65
+ );
66
+ };
@@ -2,7 +2,7 @@ import path from 'path';
2
2
  import { fileURLToPath } from 'url';
3
3
  import { defineConfig } from 'vite';
4
4
 
5
- import VitePlugin from './vite-plugin.js';
5
+ import VitePlugin from '../vite-plugin.js';
6
6
 
7
7
  const __filename = fileURLToPath(import.meta.url);
8
8
  const __dirname = path.dirname(__filename);
@@ -32,7 +32,7 @@ export default defineConfig({
32
32
  build: {
33
33
  // target: 'es2015',
34
34
  modulePreload: false,
35
- outDir: path.join(__dirname, '../../dist/www'),
35
+ outDir: path.join(__dirname, 'dist'),
36
36
  emptyOutDir: true,
37
37
  rollupOptions: {
38
38
  input: path.join(__dirname, 'index.html'),
@@ -1,3 +1,3 @@
1
- <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <path d="M8.7793 2.22668L4.97596 5.78516C4.5268 6.20541 4.5268 6.89309 4.97596 7.31334L8.7793 10.8718" stroke="white" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M10.3661 2.72003L6.01942 7.0667C5.50609 7.58003 5.50609 8.42003 6.01942 8.93336L10.3661 13.28" stroke="#1B212D" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
3
3
  </svg>
package/icons/close.svg CHANGED
@@ -1,3 +1,3 @@
1
- <svg viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <path d="M0.750078 11.3565L11.3567 0.749922M11.3567 11.3565L0.750078 0.749922" stroke="#8693B6" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
3
- </svg>
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M6.16709 17.8331L17.8333 6.16689M17.8333 17.8331L6.16709 6.16689" stroke="#262626" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>
@@ -1,3 +1,3 @@
1
- <svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <path d="M12.5573 6.03015L8.44707 10.4231C7.96166 10.942 7.16737 10.942 6.68196 10.4231L2.57178 6.03015" stroke="#8693B6" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M2.72 5.63428L7.06667 9.98094C7.58 10.4943 8.42 10.4943 8.93333 9.98094L13.28 5.63428" stroke="#1B212D" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
3
3
  </svg>
package/icons/forward.svg CHANGED
@@ -1,3 +1,3 @@
1
- <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <path d="M5.2207 2.22668L9.02404 5.78516C9.4732 6.20541 9.4732 6.89309 9.02404 7.31334L5.2207 10.8718" stroke="white" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M5.63391 2.72003L9.98058 7.0667C10.4939 7.58003 10.4939 8.42003 9.98058 8.93336L5.63391 13.28" stroke="#1B212D" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
3
3
  </svg>
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "cdui-js",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "bin": {
7
7
  "cdui-js": "./cli/bin.js"
8
8
  },
9
9
  "scripts": {
10
- "dev": "vite --config vite.config.ts"
10
+ "dev": "vite --config demo/vite.config.ts",
11
+ "icons": "npx tsx demo/icons/build.ts",
12
+ "css": "npx tsx demo/css/build.ts"
11
13
  },
12
14
  "dependencies": {
13
15
  "chalk": "^5.6.2",
@@ -0,0 +1,5 @@
1
+ import { JSX } from '../jsx';
2
+
3
+ export const Button = (props?: JSX.SvgSVGAttributes<never>) => {
4
+ return <button type="button" {...props}></button>;
5
+ };
@@ -1,13 +1,10 @@
1
- import { createSignal, splitProps } from '../reactive';
1
+ import { createSignal, combineClass, splitProps } from '../reactive';
2
2
 
3
3
  import { JSX } from '../jsx';
4
4
  import { Canleandar as i18n } from '../i18n';
5
5
  import { replaceTemplate } from '../template';
6
6
  import { For } from './For';
7
7
 
8
- // const equalsDay = ( today: Date,
9
- // selected?: Date,)
10
-
11
8
  // 渲染日期项
12
9
  const renderDates = (
13
10
  items: string[],
@@ -111,28 +108,16 @@ const renderItems = (
111
108
  return items.join('');
112
109
  };
113
110
 
114
- const SwitchMonth = (props: { onclick: () => void }) => {
115
- return (
116
- <svg
117
- class="canlendar-switch"
118
- width="16"
119
- height="16"
120
- viewBox="0 0 16 16"
121
- fill="none"
122
- xmlns="http://www.w3.org/2000/svg"
123
- onclick={props.onclick}
124
- >
125
- <path
126
- d="M10.3661 2.72003L6.01942 7.0667C5.50609 7.58003 5.50609 8.42003 6.01942 8.93336L10.3661 13.28"
127
- stroke-miterlimit="10"
128
- stroke-linecap="round"
129
- stroke-linejoin="round"
130
- />
131
- </svg>
132
- );
111
+ const switchMonth = (value: Date, offset: 1 | -1) => {
112
+ let date = new Date(value.getTime());
113
+ let month = value.getMonth();
114
+
115
+ date.setMonth(month + offset);
116
+
117
+ return date;
133
118
  };
134
119
 
135
- const parseDate = (value: Date | string | number) => {
120
+ export const parseDate = (value: Date | string | number) => {
136
121
  if (value) {
137
122
  switch (typeof value) {
138
123
  case 'number':
@@ -153,15 +138,6 @@ const formatMonth = (value: Date) => {
153
138
  return month > 9 ? month : '0' + month;
154
139
  };
155
140
 
156
- const switchMonth = (value: Date, offset: 1 | -1) => {
157
- let date = new Date(value.getTime());
158
- let month = value.getMonth();
159
-
160
- date.setMonth(month + offset);
161
-
162
- return date;
163
- };
164
-
165
141
  export const Canlendar = (
166
142
  props: Omit<JSX.HTMLAttributes<never>, 'children'> & {
167
143
  /**
@@ -183,16 +159,21 @@ export const Canlendar = (
183
159
  let domMonth: HTMLElement;
184
160
  let domBody: HTMLElement;
185
161
 
186
- const [date, setDate] = createSignal(parseDate(thisProps.value) || new Date());
162
+ const [selectedDate, setSelectedDate] = createSignal(parseDate(thisProps.value));
163
+ const [showDate, setShowDate] = createSignal(selectedDate() || new Date());
187
164
 
188
165
  return (
189
- <div class={['canlendar', thisProps.class].filter((item) => item).join(' ')} {...restProps}>
166
+ <div class={combineClass('canlendar', thisProps.class)} {...restProps}>
190
167
  <div class="canlendar-header">
191
168
  <div ref={domMonth as any} class="canlendar-month">
192
- {replaceTemplate(i18n.Month, date().getFullYear(), formatMonth(date()))}
169
+ {replaceTemplate(i18n.Month, showDate().getFullYear(), formatMonth(showDate()))}
193
170
  </div>
194
- <SwitchMonth onclick={() => setDate(switchMonth(date(), -1))}></SwitchMonth>
195
- <SwitchMonth onclick={() => setDate(switchMonth(date(), 1))}></SwitchMonth>
171
+ <svg class="icon icon-s" aria-hidden={true} onclick={() => setShowDate(switchMonth(showDate(), -1))}>
172
+ <use href="#icon-backward"></use>
173
+ </svg>
174
+ <svg class="icon icon-s" aria-hidden={true} onclick={() => setShowDate(switchMonth(showDate(), 1))}>
175
+ <use href="#icon-forward"></use>
176
+ </svg>
196
177
  </div>
197
178
  <div class="canlendar-weeks">
198
179
  <For each={i18n.Weeks}>{(item) => <span>{item}</span>}</For>
@@ -209,17 +190,14 @@ export const Canlendar = (
209
190
 
210
191
  if (dom !== target) {
211
192
  date = date.split('|');
212
- onValueChange(new Date(date[0] | 0, date[1] | 0, date[2] | 0));
213
-
214
- if (dom) {
215
- dom.classList.remove('selected');
216
- }
193
+ date = new Date(date[0] | 0, date[1] | 0, date[2] | 0);
217
194
 
218
- target.classList.add('selected');
195
+ setSelectedDate(date);
196
+ onValueChange(date);
219
197
  }
220
198
  }
221
199
  }}
222
- innerHTML={renderItems(date(), parseDate(thisProps.value), thisProps.disableDate)}
200
+ innerHTML={renderItems(showDate(), selectedDate(), thisProps.disableDate)}
223
201
  ></div>
224
202
  </div>
225
203
  );
@@ -1,9 +1,18 @@
1
- import { createEffect, createMemo, createSignal, For, onCleanup, onMount, splitProps } from 'solid-js';
2
-
3
1
  import { JSX } from '../jsx';
4
2
  import { isBrowser } from '../dom';
5
3
  import { animateScrollIntoView } from '../animate-scroll-to';
6
- import { defineProperty } from '../reactive';
4
+ import {
5
+ createEffect,
6
+ createMemo,
7
+ createSignal,
8
+ combineClass,
9
+ defineProperty,
10
+ onCleanup,
11
+ onMount,
12
+ splitProps,
13
+ } from '../reactive';
14
+
15
+ import { For } from './For';
7
16
  import { Icon } from './Icon';
8
17
 
9
18
  const CLASS_NAME = 'carousel-vertical';
@@ -269,7 +278,7 @@ export const Carousel = <T, U extends JSX.Element>(
269
278
  });
270
279
 
271
280
  return (
272
- <div ref={ref as any} class={['carousel scrollbar-hidden', thisProps.class].filter(item => item).join(' ')} {...restProps}>
281
+ <div ref={ref as any} class={combineClass('carousel scrollbar-hidden', thisProps.class)} {...restProps}>
273
282
  <For each={fillItems(thisProps.each)}>{thisProps.children}</For>
274
283
  </div>
275
284
  );
@@ -1,7 +1,7 @@
1
1
  import { createSignal, splitProps } from 'solid-js';
2
2
 
3
3
  import { JSX } from '../jsx';
4
- import { defineProperty } from '../reactive';
4
+ import { combineClass, defineProperty } from '../reactive';
5
5
 
6
6
  const COLLAPSED_CLASS = 'collapsed';
7
7
 
@@ -130,9 +130,7 @@ export const CollapsiblePanel = (
130
130
  return (
131
131
  <div
132
132
  ref={ref as any}
133
- class={['collapsed-panel', thisProps.collapsed && COLLAPSED_CLASS, thisProps.class]
134
- .filter((item) => item)
135
- .join(' ')}
133
+ class={combineClass('collapsed-panel', thisProps.collapsed && COLLAPSED_CLASS, thisProps.class)}
136
134
  {...restProps}
137
135
  ></div>
138
136
  );
@@ -1,162 +1,66 @@
1
- import { splitProps } from '../reactive';
2
1
  import { JSX } from '../jsx';
3
-
4
- import { disableAutoCloseEvent, registerAutoClose } from '../dom';
5
-
6
- const POPUP_TOP_CLASS = 'combobox-popup-top';
7
- const POPUP_RIGHT_CLASS = 'combobox-popup-right';
8
-
9
- /**
10
- * 当前弹出层
11
- */
12
- let currentPopup: ComboBoxApi;
13
-
14
- /**
15
- * 关闭当前弹出层
16
- */
17
- export const closePopup = () => {
18
- currentPopup && currentPopup.closePupup();
19
- };
20
-
21
- /**
22
- * 是否使用下拉动画
23
- */
24
- export let useDropdownTransition = true;
25
-
26
- /**
27
- * 设置是否使用下拉动画
28
- *
29
- * @param use 是否使用
30
- */
31
- export const setUseDropdownTransition = (use: boolean) => {
32
- useDropdownTransition = use;
33
- };
34
-
35
- // 注册点击关闭弹出层的方法
36
- registerAutoClose(closePopup);
37
-
38
- /**
39
- * 下拉框组件外部访问接口
40
- */
41
- export interface ComboBoxApi {
42
- /**
43
- * 打开弹出框
44
- */
45
- openPopup(): void;
46
- /**
47
- * 关闭弹出框
48
- */
49
- closePupup(): void;
50
- }
2
+ import { combineClass, splitProps } from '../reactive';
3
+ import { disableAutoCloseEvent } from '../dom';
4
+ import { Popup, PopupApi, PopupProps } from './Popup';
51
5
 
52
6
  /**
53
7
  * 下拉框组件
54
8
  */
55
- export const ComboBox = <T, U extends JSX.Element>(
56
- props?: JSX.HTMLAttributes<never> & {
57
- /**
58
- * 值
59
- */
60
- value?: string;
61
- /**
62
- * 是否只读
63
- */
64
- readonly?: boolean;
65
- /**
66
- * 外部调用接口
67
- */
68
- api?: (api: ComboBoxApi) => void;
69
- },
9
+ export const ComboBox = (
10
+ props?: JSX.HTMLAttributes<never> &
11
+ PopupProps & {
12
+ /**
13
+ * 值
14
+ */
15
+ value?: string;
16
+ /**
17
+ * 是否只读
18
+ */
19
+ readonly?: boolean;
20
+ /**
21
+ * 获取焦点时是否自动弹出
22
+ */
23
+ popupOnFocus?: boolean;
24
+ /**
25
+ * 弹出层属性
26
+ */
27
+ popup?: Omit<JSX.HTMLAttributes<never>, 'children'>;
28
+ },
70
29
  ) => {
71
- let [thisProps, restProps] = splitProps(props, ['class', 'value', 'readonly', 'api', 'children']);
72
- let popup: HTMLElement;
73
- let opened: boolean;
74
-
75
- const toggle = (event?: Event) => {
76
- if (popup) {
77
- let combobox = popup.parentNode as HTMLElement;
78
- let rect = combobox.getBoundingClientRect();
79
- let style = popup.style;
80
-
81
- if ((opened = !opened)) {
82
- let windowWidth = window.innerWidth;
83
- let windowHeight = window.innerHeight;
84
- let classList = popup.classList;
85
-
86
- if (currentPopup) {
87
- currentPopup.closePupup();
88
- }
89
-
90
- // 设置当前弹出层
91
- currentPopup = api;
92
-
93
- style.height = 'auto';
94
- style.display = 'block';
95
-
96
- let height = popup.offsetHeight;
97
-
98
- if (windowHeight - rect.top - rect.height < height + 4 && rect.top >= height) {
99
- classList.add(POPUP_TOP_CLASS);
100
- } else {
101
- classList.remove(POPUP_TOP_CLASS);
102
- }
103
-
104
- if (windowWidth - rect.left - rect.width >= 0) {
105
- classList.add(POPUP_RIGHT_CLASS);
106
- } else {
107
- classList.remove(POPUP_RIGHT_CLASS);
108
- }
109
-
110
- if (useDropdownTransition) {
111
- style.height = '0';
112
-
113
- setTimeout(() => {
114
- style.height = height + 'px';
115
- });
116
- }
117
- } else {
118
- currentPopup = null;
119
-
120
- if (useDropdownTransition) {
121
- style.height = popup.offsetHeight + 'px';
122
-
123
- setTimeout(() => {
124
- style.height = '0';
125
- });
126
- } else {
127
- style.display = 'none';
128
- }
129
- }
130
- }
131
-
132
- event && event.stopPropagation();
30
+ let [thisProps, restProps] = splitProps(props, [
31
+ 'class',
32
+ 'value',
33
+ 'readonly',
34
+ 'popupOnFocus',
35
+ 'popup',
36
+ 'onPopup',
37
+ 'api',
38
+ 'children',
39
+ ]);
40
+ let popup: PopupApi;
41
+
42
+ const initApi = (api) => {
43
+ popup = api;
44
+ thisProps.api && thisProps.api(popup);
133
45
  };
134
46
 
135
- const api = {
136
- openPopup: () => opened || toggle(),
137
- closePupup: () => opened && toggle(),
138
- };
139
-
140
- // 初始化外部调用接口
141
- props.api && props.api(api);
142
-
143
47
  return (
144
- <div class={['combobox', thisProps.class].filter((item) => item).join(' ')} {...restProps}>
145
- <div class="combobox-host" {...disableAutoCloseEvent} onclick={() => thisProps.readonly && toggle()}>
146
- <input value={thisProps.value ?? ''} readonly={thisProps.readonly} style={{ border: 'none' }}></input>
147
- <svg class="icon icon-s" aria-hidden={true} style={{ height: '100%', padding: '0 4px' }} onclick={toggle}>
48
+ <div class={combineClass('combobox', thisProps.class)} {...restProps}>
49
+ <div class="combobox-host" {...disableAutoCloseEvent}>
50
+ <input
51
+ class="combobox-input"
52
+ value={thisProps.value || ''}
53
+ readonly={thisProps.readonly}
54
+ onfocus={() => thisProps.popupOnFocus && popup.openPopup()}
55
+ onclick={() => thisProps.readonly && !thisProps.popupOnFocus && popup.togglePopup()}
56
+ ></input>
57
+ <svg class="icon icon-s" aria-hidden={true} onclick={() => popup.togglePopup()}>
148
58
  <use href="#icon-dropdown"></use>
149
59
  </svg>
150
60
  </div>
151
- <div
152
- ref={popup as any}
153
- class="combobox-popup"
154
- style={{ display: 'none' }}
155
- {...disableAutoCloseEvent}
156
- ontransitionend={() => opened || (popup.style.display = 'none')}
157
- >
61
+ <Popup api={initApi} onPopup={thisProps.onPopup} {...thisProps.popup}>
158
62
  {thisProps.children}
159
- </div>
63
+ </Popup>
160
64
  </div>
161
65
  );
162
66
  };
@@ -0,0 +1,91 @@
1
+ import { JSX } from '../jsx';
2
+ import { combineClass, createSignal, splitProps } from '../reactive';
3
+ import { disableAutoCloseEvent } from '../dom';
4
+ import { For } from './For';
5
+ import { Popup, PopupApi } from './Popup';
6
+ import { parseDate } from './Canlendar';
7
+
8
+ const formatDate = (date: Date, format: string) => {
9
+ return date ? date.toLocaleString() : '';
10
+ };
11
+
12
+ const showPopup = () => {};
13
+
14
+ const MobileTouchScroll = (props: { items: (number | string)[] }) => {
15
+ return (
16
+ <div>
17
+ <For each={props.items}>{(item) => <div>{item}</div>}</For>
18
+ </div>
19
+ );
20
+ };
21
+
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
+ /**
50
+ * 日期选择组件
51
+ */
52
+ export const DatePicker = (
53
+ props?: JSX.HTMLAttributes<never> & {
54
+ /**
55
+ * 值
56
+ */
57
+ value?: string | number | Date;
58
+ /**
59
+ * 日期格式
60
+ */
61
+ format?: string;
62
+ /**
63
+ * 是否只读
64
+ */
65
+ readonly?: boolean;
66
+ },
67
+ ) => {
68
+ let [thisProps, restProps] = splitProps(props, ['class', 'value', 'readonly', 'format', 'children']);
69
+ let popup: PopupApi;
70
+
71
+ const [value, setValue] = createSignal(thisProps.value && parseDate(thisProps.value));
72
+
73
+ return (
74
+ <div class={combineClass('datepicker', thisProps.class)} {...restProps}>
75
+ <div class="datepicker-host" {...disableAutoCloseEvent}>
76
+ <input
77
+ class="datepicker-input"
78
+ value={formatDate(value(), thisProps.format)}
79
+ readonly={thisProps.readonly}
80
+ onclick={() => thisProps.readonly && popup.togglePopup()}
81
+ ></input>
82
+ <svg class="icon icon-s" aria-hidden={true} onclick={() => popup.togglePopup()}>
83
+ <use href="#icon-dropdown"></use>
84
+ </svg>
85
+ </div>
86
+ <Popup api={(api) => (popup = api)} onPopup={() => showPopup()}>
87
+ {thisProps.children}
88
+ </Popup>
89
+ </div>
90
+ );
91
+ };
@@ -16,7 +16,7 @@ export type Dialog = HTMLElement & {
16
16
  * 显示对话框
17
17
  *
18
18
  * @param component 对话框组件
19
- * @returns 关闭对话框方法
19
+ * @returns 对话框对象
20
20
  */
21
21
  export const showDialog = (component: () => JSX.Element): Dialog => {
22
22
  return createRoot((dispose) => {