antd-solid 0.0.11 → 0.0.12

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 (50) hide show
  1. package/css/index.css +9 -5
  2. package/dist/index.esm.js +232 -135
  3. package/dist/index.umd.js +1 -1
  4. package/es/{Button.d.ts → Button/index.d.ts} +3 -2
  5. package/es/{Button.js → Button/index.js} +11 -9
  6. package/es/Button/index.scss.js +6 -0
  7. package/es/{Compact.js → Compact/index.js} +4 -4
  8. package/es/Drawer/index.js +2 -3
  9. package/es/Empty/index.d.ts +6 -2
  10. package/es/Empty/index.js +7 -2
  11. package/es/Input.js +3 -3
  12. package/es/InputNumber.js +1 -1
  13. package/es/Modal.d.ts +4 -5
  14. package/es/Modal.js +10 -9
  15. package/es/Popconfirm.js +1 -1
  16. package/es/Progress/index.d.ts +1 -2
  17. package/es/Progress/index.js +18 -8
  18. package/es/Radio.js +2 -2
  19. package/es/Result.js +1 -1
  20. package/es/Select.js +2 -2
  21. package/es/Spin.d.ts +1 -0
  22. package/es/Spin.js +12 -6
  23. package/es/Switch.js +1 -1
  24. package/es/Tabs.js +2 -2
  25. package/es/Timeline.js +1 -1
  26. package/es/Tree.js +2 -2
  27. package/es/Upload.d.ts +56 -10
  28. package/es/Upload.js +93 -3
  29. package/es/index.js +2 -2
  30. package/package.json +1 -1
  31. package/src/Button/index.scss +9 -0
  32. package/src/{Button.tsx → Button/index.tsx} +18 -9
  33. package/src/Compact/index.tsx +20 -0
  34. package/src/Drawer/index.tsx +1 -2
  35. package/src/Empty/index.tsx +11 -6
  36. package/src/Input.tsx +2 -2
  37. package/src/InputNumber.tsx +34 -20
  38. package/src/Modal.tsx +14 -13
  39. package/src/Progress/index.tsx +17 -9
  40. package/src/Radio.tsx +3 -3
  41. package/src/Result.tsx +1 -1
  42. package/src/Select.tsx +14 -4
  43. package/src/Spin.tsx +16 -5
  44. package/src/Switch.tsx +1 -1
  45. package/src/Tabs.tsx +2 -2
  46. package/src/Timeline.tsx +1 -1
  47. package/src/Tree.tsx +4 -3
  48. package/src/Upload.tsx +138 -5
  49. package/src/Compact.tsx +0 -15
  50. /package/es/{Compact.d.ts → Compact/index.d.ts} +0 -0
package/src/Input.tsx CHANGED
@@ -34,7 +34,7 @@ const statusClassDict = {
34
34
  cs(
35
35
  'ant-[border:1px_solid_var(--ant-color-border)]',
36
36
  !disabled &&
37
- 'hover:ant-border-[var(--primary-color)] focus-within:ant-border-[var(--primary-color)] focus-within:ant-[box-shadow:0_0_0_2px_rgba(5,145,255,0.1)]',
37
+ 'hover:ant-border-[var(--ant-color-primary)] focus-within:ant-border-[var(--ant-color-primary)] focus-within:ant-[box-shadow:0_0_0_2px_rgba(5,145,255,0.1)]',
38
38
  ),
39
39
  error: (disabled: boolean) =>
40
40
  cs(
@@ -138,7 +138,7 @@ export function CommonInput<T extends HTMLInputElement | HTMLTextAreaElement = H
138
138
  <Show when={hasPrefixOrSuffix()} fallback={inputJSX}>
139
139
  <div
140
140
  class={cs(
141
- 'ant-flex ant-items-center ant-w-full ant-relative ant-[--input-after-display:none] hover:ant-[--input-after-display:block] p:hover-child[input]:ant-border-[var(--primary-color)]',
141
+ 'ant-flex ant-items-center ant-w-full ant-relative ant-[--input-after-display:none] hover:ant-[--input-after-display:block] p:hover-child[input]:ant-border-[var(--ant-color-primary)]',
142
142
  inputWrapClass(),
143
143
  )}
144
144
  >
@@ -1,4 +1,12 @@
1
- import { type Component, createEffect, on, splitProps, untrack, createSignal, mergeProps } from 'solid-js'
1
+ import {
2
+ type Component,
3
+ createEffect,
4
+ on,
5
+ splitProps,
6
+ untrack,
7
+ createSignal,
8
+ mergeProps,
9
+ } from 'solid-js'
2
10
  import { CommonInput, type InputProps } from './Input'
3
11
  import { clamp, isNil } from 'lodash-es'
4
12
  import { dispatchEventHandlerUnion } from './utils/solid'
@@ -18,22 +26,20 @@ export interface InputNumberProps
18
26
  const isEmptyValue = (value: number | string | null | undefined) => isNil(value) || value === ''
19
27
 
20
28
  const actionBtnClass =
21
- 'ant-text-12px ant-flex ant-justify-center ant-items-center ant-h-1/2 ant-cursor-pointer ant-opacity-70 hover:ant-h-100% hover:ant-text-[var(--primary-color)] ant-transition-color ant-transition-height ant-transition-duration-500'
29
+ 'ant-text-12px ant-flex ant-justify-center ant-items-center ant-h-1/2 ant-cursor-pointer ant-opacity-70 hover:ant-h-100% hover:ant-text-[var(--ant-color-primary)] ant-transition-color ant-transition-height ant-transition-duration-500'
22
30
 
23
31
  const InputNumber: Component<InputNumberProps> = _props => {
24
- const props = mergeProps({
25
- min: -Infinity,
26
- max: Infinity,
27
- }, _props)
28
- const [_, inputProps] = splitProps(props, [
29
- 'defaultValue',
30
- 'value',
31
- 'onChange',
32
- 'onBlur',
33
- ])
32
+ const props = mergeProps(
33
+ {
34
+ min: -Infinity,
35
+ max: Infinity,
36
+ },
37
+ _props,
38
+ )
39
+ const [_, inputProps] = splitProps(props, ['defaultValue', 'value', 'onChange', 'onBlur'])
34
40
 
35
41
  const clampValue = (v: number) => untrack(() => clamp(v, props.min, props.max))
36
-
42
+
37
43
  let prev: number | null = null
38
44
  const updatePrev = (v: number | null) => {
39
45
  if (prev === v) return
@@ -42,12 +48,20 @@ const InputNumber: Component<InputNumberProps> = _props => {
42
48
  props.onChange?.(prev)
43
49
  }
44
50
 
45
- const [value, setValue] = createSignal<number | string | null | undefined>(untrack(() => props.value ?? props.defaultValue))
46
- createEffect(on(() => props.value, () => {
47
- setValue(props.value)
48
- }, {
49
- defer: true
50
- }))
51
+ const [value, setValue] = createSignal<number | string | null | undefined>(
52
+ untrack(() => props.value ?? props.defaultValue),
53
+ )
54
+ createEffect(
55
+ on(
56
+ () => props.value,
57
+ () => {
58
+ setValue(props.value)
59
+ },
60
+ {
61
+ defer: true,
62
+ },
63
+ ),
64
+ )
51
65
 
52
66
  const add = (addon: number) => {
53
67
  let newValue: number | null
@@ -104,7 +118,7 @@ const InputNumber: Component<InputNumberProps> = _props => {
104
118
 
105
119
  let newValueNum: number | null = Number(newValue)
106
120
  if (Number.isNaN(newValueNum)) return
107
-
121
+
108
122
  if (isEmptyValue(newValue)) {
109
123
  newValueNum = null
110
124
  } else {
package/src/Modal.tsx CHANGED
@@ -6,11 +6,6 @@ import cs from 'classnames'
6
6
  export interface ModalInstance {
7
7
  open: () => void
8
8
  close: () => void
9
- /**
10
- * 与 close 相似,但关闭时会销毁 Modal 里的子元素
11
- * @returns
12
- */
13
- destroy: () => void
14
9
  }
15
10
 
16
11
  export interface ModalProps {
@@ -34,6 +29,10 @@ export interface ModalProps {
34
29
  */
35
30
  closeIcon?: boolean
36
31
  footer?: boolean | ((modal: ModalInstance) => JSXElement)
32
+ /**
33
+ * 关闭时销毁 Modal 里的子元素
34
+ */
35
+ destroyOnClose?: boolean
37
36
  /**
38
37
  * 返回 true,会自动关闭 modal
39
38
  */
@@ -75,14 +74,16 @@ function Modal(_props: ModalProps) {
75
74
  }
76
75
  },
77
76
  close() {
78
- setHide(true)
79
- cleanup()
80
- props.afterClose?.()
81
- },
82
- destroy() {
83
- setOpen(false)
84
- cleanup()
85
- props.afterClose?.()
77
+ untrack(() => {
78
+ if (props.destroyOnClose) {
79
+ setOpen(false)
80
+ } else {
81
+ setHide(true)
82
+ }
83
+
84
+ cleanup()
85
+ props.afterClose?.()
86
+ })
86
87
  },
87
88
  }
88
89
  untrack(() => {
@@ -1,4 +1,4 @@
1
- import { mergeProps, type Component, Switch, Match, Show } from 'solid-js'
1
+ import { mergeProps, type Component, Switch, Match, Show, createMemo } from 'solid-js'
2
2
  import cs from 'classnames'
3
3
 
4
4
  export interface ProgressProps {
@@ -9,9 +9,8 @@ export interface ProgressProps {
9
9
  percent?: number
10
10
  /**
11
11
  * 状态
12
- * 默认 'default'
13
12
  */
14
- status?: 'default' | 'success' | 'error'
13
+ status?: 'normal' | 'success' | 'error'
15
14
  /**
16
15
  * 'line' 类型进度条的高度
17
16
  * 默认 8
@@ -28,13 +27,17 @@ const Progress: Component<ProgressProps> = _props => {
28
27
  const props = mergeProps(
29
28
  {
30
29
  percent: 0,
31
- status: 'default',
32
30
  height: 8,
33
31
  showInfo: true,
34
- } as Required<ProgressProps>,
32
+ },
35
33
  _props,
36
34
  )
37
35
 
36
+ const status = createMemo(() => {
37
+ if (props.status) return props.status
38
+ return props.percent >= 100 ? 'success' : 'normal'
39
+ })
40
+
38
41
  return (
39
42
  <div
40
43
  class="ant-flex ant-items-center"
@@ -45,22 +48,27 @@ const Progress: Component<ProgressProps> = _props => {
45
48
  <div
46
49
  class={cs(
47
50
  'ant-w-full ant-bg-[var(--ant-progress-remaining-color)]',
48
- 'before:ant-content-empty before:ant-block before:ant-bg-[var(--primary-color)] before:ant-w-[var(--percent)] before:ant-h-full before:ant-rounded-inherit',
51
+ 'before:ant-content-empty before:ant-block before:ant-bg-[var(--color)] before:ant-w-[var(--percent)] before:ant-h-full before:ant-rounded-inherit',
49
52
  )}
50
53
  style={{
51
54
  height: `${props.height}px`,
52
55
  'border-radius': `${props.height / 2}px`,
53
56
  '--percent': `${props.percent}%`,
57
+ '--color': {
58
+ normal: 'var(--ant-color-primary)',
59
+ success: 'var(--ant-color-success)',
60
+ error: 'var(--ant-color-error)',
61
+ }[status()],
54
62
  }}
55
63
  />
56
64
 
57
65
  <Show when={props.showInfo}>
58
66
  <span class="ant-shrink-0 ant-min-w-40px ant-ml-8px ant-text-center">
59
67
  <Switch fallback={`${props.percent}%`}>
60
- <Match when={props.status === 'success' || props.percent >= 100}>
61
- <span class="i-ant-design:check-circle-filled ant-text-[var(--primary-color)]" />
68
+ <Match when={status() === 'success'}>
69
+ <span class="i-ant-design:check-circle-filled ant-text-[var(--ant-color-success)]" />
62
70
  </Match>
63
- <Match when={props.status === 'error'}>
71
+ <Match when={status() === 'error'}>
64
72
  <span class="i-ant-design:close-circle-filled ant-text-[var(--ant-color-error)]" />
65
73
  </Match>
66
74
  </Switch>
package/src/Radio.tsx CHANGED
@@ -47,7 +47,7 @@ const Radio: Component<RadioProps> & {
47
47
  <span
48
48
  class={cs(
49
49
  'ant-w-16px ant-h-16px ant-rounded-50% ant-[border:1px_solid_var(--ant-color-border)]',
50
- checked() && 'ant-[border:5px_solid_var(--primary-color)]',
50
+ checked() && 'ant-[border:5px_solid_var(--ant-color-primary)]',
51
51
  )}
52
52
  >
53
53
  <input
@@ -77,9 +77,9 @@ Radio.Button = props => {
77
77
  return (
78
78
  <label
79
79
  class={cs(
80
- 'ant-px-15px ant-[border:1px_solid_rgb(217,217,217)] first:ant-rounded-l-6px last:ant-rounded-r-6px ant-h-32px ant-inline-flex ant-items-center hover:ant-text-[var(--primary-color)] not[:last-child]:ant-border-r-transparent ant-cursor-pointer ant-flex-grow ant-justify-center',
80
+ 'ant-px-15px ant-[border:1px_solid_rgb(217,217,217)] first:ant-rounded-l-6px last:ant-rounded-r-6px ant-h-32px ant-inline-flex ant-items-center hover:ant-text-[var(--ant-color-primary)] not[:last-child]:ant-border-r-transparent ant-cursor-pointer ant-flex-grow ant-justify-center',
81
81
  checked() &&
82
- 'ant-text-[var(--primary-color)] ant-[border:1px_solid_var(--primary-color)] !ant-border-r-[var(--primary-color)]',
82
+ 'ant-text-[var(--ant-color-primary)] ant-[border:1px_solid_var(--ant-color-primary)] !ant-border-r-[var(--ant-color-primary)]',
83
83
  )}
84
84
  >
85
85
  <input
package/src/Result.tsx CHANGED
@@ -12,7 +12,7 @@ export interface ResultProps extends ParentProps {
12
12
 
13
13
  const statusIconMap = {
14
14
  success: 'ant-text-#52c41a i-ant-design:check-circle-filled',
15
- info: 'ant-text-[var(--primary-color)] i-ant-design:exclamation-circle-filled',
15
+ info: 'ant-text-[var(--ant-color-primary)] i-ant-design:exclamation-circle-filled',
16
16
  warning: 'ant-text-#faad14 i-ant-design:warning-filled',
17
17
  error: 'ant-text-#ff4d4f i-ant-design:close-circle-filled',
18
18
  }
package/src/Select.tsx CHANGED
@@ -33,7 +33,9 @@ const Select: Component<SelectProps> = props => {
33
33
 
34
34
  const [value, setValue] = createControllableValue<Key | undefined>(props)
35
35
  const selectedValue = createSelector(value)
36
- const selectedOption = createMemo(() => !isNil(value()) ? props.options.find(option => option.value === value()) : undefined)
36
+ const selectedOption = createMemo(() =>
37
+ !isNil(value()) ? props.options.find(option => option.value === value()) : undefined,
38
+ )
37
39
 
38
40
  const [open, setOpen] = createSignal(false)
39
41
  useClickAway(
@@ -63,7 +65,7 @@ const Select: Component<SelectProps> = props => {
63
65
  <div
64
66
  class={cs(
65
67
  'ant-box-content ant-px-12px ant-py-5px ant-h-22px ant-leading-22px hover:ant-bg-[var(--hover-bg-color)]',
66
- selectedValue(item.value) ? '!ant-bg-[var(--active-bg-color)]' : '',
68
+ selectedValue(item.value) ? '!ant-bg-[var(--ant-select-option-selected-bg)]' : '',
67
69
  )}
68
70
  onClick={() => {
69
71
  setValue(item.value)
@@ -79,7 +81,10 @@ const Select: Component<SelectProps> = props => {
79
81
  >
80
82
  <div
81
83
  ref={select!}
82
- class={cs('ant-h-32px ant-leading-32px ant-rounded-6px ant-[border:1px_solid_var(--ant-color-border)] ant-px-11px focus:ant-[border-color:var(--primary-color)]', props.class)}
84
+ class={cs(
85
+ 'ant-h-32px ant-leading-32px ant-rounded-6px ant-[border:1px_solid_var(--ant-color-border)] ant-px-11px focus:ant-[border-color:var(--ant-color-primary)]',
86
+ props.class,
87
+ )}
83
88
  tabIndex="0"
84
89
  onClick={e => {
85
90
  setOpen(true)
@@ -109,7 +114,12 @@ const Select: Component<SelectProps> = props => {
109
114
  </Show>
110
115
 
111
116
  <div class="ant-absolute ant-top-0 ant-bottom-0 ant-right-0">
112
- <Show when={showClearBtn()} fallback={<span class="i-ant-design:down-outlined ant-text-[var(--ant-allow-color)]" />}>
117
+ <Show
118
+ when={showClearBtn()}
119
+ fallback={
120
+ <span class="i-ant-design:down-outlined ant-text-[var(--ant-allow-color)]" />
121
+ }
122
+ >
113
123
  <span
114
124
  class="i-ant-design:close-circle-filled ant-cursor-pointer ant-text-[var(--ant-clear-color)] hover:ant-text-[var(--ant-clear-color-hover)]"
115
125
  onClick={e => {
package/src/Spin.tsx CHANGED
@@ -1,19 +1,30 @@
1
- import { Show, type Component, type ParentProps } from 'solid-js'
1
+ import { Show, type Component, type ParentProps, mergeProps } from 'solid-js'
2
2
 
3
3
  interface SpinProps extends ParentProps {
4
4
  /**
5
5
  * 是否为加载中状态
6
6
  */
7
7
  spinning?: boolean
8
+ size?: number
8
9
  }
9
10
 
10
- const Spin: Component<SpinProps> = props => {
11
+ const Spin: Component<SpinProps> = _props => {
12
+ const props = mergeProps(
13
+ {
14
+ size: 20,
15
+ },
16
+ _props,
17
+ )
18
+
11
19
  return (
12
- <div class="ant-relative ant-min-h-32px">
20
+ <div>
13
21
  {props.children}
14
22
  <Show when={props.spinning}>
15
- <div class="ant-absolute ant-inset-0 ant-flex ant-items-center ant-justify-center ant-bg-[rgba(255,255,255,.5)]">
16
- <span class="i-ant-design:loading keyframes-spin ant-[animation:spin_1s_linear_infinite] ant-text-32px ant-text-[var(--primary-color)]" />
23
+ <div class="ant-flex ant-items-center ant-justify-center ant-bg-[rgba(255,255,255,.5)]">
24
+ <span
25
+ class="i-ant-design:loading keyframes-spin ant-[animation:spin_1s_linear_infinite] ant-text-[var(--ant-color-primary)]"
26
+ style={{ 'font-size': `${props.size}px` }}
27
+ />
17
28
  </div>
18
29
  </Show>
19
30
  </div>
package/src/Switch.tsx CHANGED
@@ -17,7 +17,7 @@ const Switch: Component<SwitchProps> = props => {
17
17
  <button
18
18
  class={cs(
19
19
  'ant-w-44px ant-h-22px ant-rounded-100px ant-relative',
20
- checked() ? 'ant-bg-[var(--primary-color)]' : 'ant-bg-[rgba(0,0,0,0.45)]',
20
+ checked() ? 'ant-bg-[var(--ant-color-primary)]' : 'ant-bg-[rgba(0,0,0,0.45)]',
21
21
  )}
22
22
  onClick={() => setChecked(c => !c)}
23
23
  >
package/src/Tabs.tsx CHANGED
@@ -92,7 +92,7 @@ const Tabs: Component<TabsProps> = _props => {
92
92
  class={cs(
93
93
  'ant-py-12px ant-cursor-pointer',
94
94
  props.navItemClass,
95
- isSelectedItem(item.key) && 'ant-text-[var(--primary-color)] selected',
95
+ isSelectedItem(item.key) && 'ant-text-[var(--ant-color-primary)] selected',
96
96
  )}
97
97
  onClick={() => {
98
98
  setSelectedItem(item)
@@ -106,7 +106,7 @@ const Tabs: Component<TabsProps> = _props => {
106
106
 
107
107
  <div
108
108
  role={'selected-bar' as any}
109
- class="ant-absolute ant-bottom-0 ant-bg-[var(--primary-color)] ant-h-2px ant-transition-left"
109
+ class="ant-absolute ant-bottom-0 ant-bg-[var(--ant-color-primary)] ant-h-2px ant-transition-left"
110
110
  style={selectedBarStyle()}
111
111
  />
112
112
  </div>
package/src/Timeline.tsx CHANGED
@@ -21,7 +21,7 @@ const Timeline: Component<TimelineProps> = props => {
21
21
  {i() !== props.items.length - 1 && (
22
22
  <div class="ant-absolute ant-top-[8px] ant-bottom-[-24px] ant-left-[4px] ant-w-[2px] ant-bg-[rgba(5,5,5,.06)]" />
23
23
  )}
24
- <div class="ant-w-[10px] ant-h-[10px] ant-border-solid ant-border-width-[3px] ant-border-[var(--primary-color)] ant-bg-white ant-rounded-[50%] ant-mt-[8px]" />
24
+ <div class="ant-w-[10px] ant-h-[10px] ant-border-solid ant-border-width-[3px] ant-border-[var(--ant-color-primary)] ant-bg-white ant-rounded-[50%] ant-mt-[8px]" />
25
25
  <div class="ant-ml-[8px]">{item.children?.()}</div>
26
26
  </div>
27
27
  )}
package/src/Tree.tsx CHANGED
@@ -98,7 +98,8 @@ function SingleLevelTree<T extends {} = {}>(props: SingleLevelTreeProps<T>) {
98
98
  <div
99
99
  class={cs(
100
100
  'ant-flex ant-items-center ant-h-28px ant-pb-4px',
101
- isDraggable(item()) && 'ant-[border:1px_solid_var(--primary-color)] ant-bg-white',
101
+ isDraggable(item()) &&
102
+ 'ant-[border:1px_solid_var(--ant-color-primary)] ant-bg-white',
102
103
  draggableNode() && 'child[]:ant-pointer-events-none',
103
104
  )}
104
105
  draggable={draggable}
@@ -168,9 +169,9 @@ function SingleLevelTree<T extends {} = {}>(props: SingleLevelTreeProps<T>) {
168
169
  class={cs(
169
170
  'ant-h-full ant-leading-24px hover:ant-bg-[var(--hover-bg-color)] ant-rounded-1 ant-px-1 ant-cursor-pointer ant-relative',
170
171
  props.blockNode && 'w-full',
171
- selectedNodes()?.includes(item()) && '!ant-bg-[var(--active-bg-color)]',
172
+ selectedNodes()?.includes(item()) && '!ant-bg-[var(--ant-tree-node-selected-bg)]',
172
173
  isTarget(item()) &&
173
- "before:ant-content-[''] before:ant-inline-block before:ant-w-8px before:ant-h-8px before:ant-absolute before:ant-bottom-0 before:ant-left-0 before:-ant-translate-x-full before:ant-translate-y-1/2 before:ant-rounded-1/2 before:ant-[border:2px_solid_var(--primary-color)] after:ant-content-[''] after:ant-inline-block after:ant-h-2px after:ant-absolute after:ant-left-0 after:ant-right-0 after:ant-bottom--1px after:ant-bg-[var(--primary-color)]",
174
+ "before:ant-content-[''] before:ant-inline-block before:ant-w-8px before:ant-h-8px before:ant-absolute before:ant-bottom-0 before:ant-left-0 before:-ant-translate-x-full before:ant-translate-y-1/2 before:ant-rounded-1/2 before:ant-[border:2px_solid_var(--ant-color-primary)] after:ant-content-[''] after:ant-inline-block after:ant-h-2px after:ant-absolute after:ant-left-0 after:ant-right-0 after:ant-bottom--1px after:ant-bg-[var(--ant-color-primary)]",
174
175
  )}
175
176
  onClick={() => {
176
177
  setSelectedNodes([item()])
package/src/Upload.tsx CHANGED
@@ -1,10 +1,143 @@
1
- import { reactToSolidComponent, replaceChildren, replaceClassName } from './utils/component'
2
- import { Upload as UploadAntd } from 'antd'
1
+ import { nanoid } from 'nanoid'
2
+ import { type Component, mergeProps, type ParentProps, createSignal, type Signal } from 'solid-js'
3
3
 
4
- export type { UploadFile } from 'antd/es/upload'
4
+ interface UploadProgressEvent extends Partial<ProgressEvent> {
5
+ percent?: number
6
+ }
5
7
 
6
- const Upload = replaceChildren(replaceClassName(reactToSolidComponent(UploadAntd)))
8
+ export interface UploadRequestOption<T = any> {
9
+ file: File
10
+ onProgress?: (event: UploadProgressEvent) => void
11
+ onError?: (event: Error) => void
12
+ onSuccess?: (body: T) => void
13
+ }
7
14
 
8
- export type UploadProps = Parameters<typeof Upload>[0]
15
+ export interface UploadFile<T = any> {
16
+ /**
17
+ * 文件唯一标识
18
+ */
19
+ id: string
20
+ /**
21
+ * 文件名
22
+ */
23
+ name: string
24
+ /**
25
+ * MIME 类型
26
+ */
27
+ type?: string
28
+ /**
29
+ * 文件大小
30
+ */
31
+ size?: number
32
+ file?: File
33
+ response?: T
34
+ status?: 'pending' | 'uploading' | 'error' | 'finished'
35
+ percent?: number
36
+ /**
37
+ * 下载地址
38
+ */
39
+ url?: string
40
+ }
41
+
42
+ export interface UploadProps<T = any> extends ParentProps {
43
+ class?: string
44
+ accept?: string
45
+ /**
46
+ * 上传的地址
47
+ */
48
+ action?: string
49
+ /**
50
+ * 上传请求的 http method
51
+ * 默认 'post'
52
+ */
53
+ method?: string
54
+ customRequest?: (option: UploadRequestOption<T>) => void
55
+ onAdd?: (fileList: Array<Signal<T>>) => void
56
+ multiple?: boolean
57
+ }
58
+
59
+ function request(file: File, customRequest: UploadProps['customRequest']) {
60
+ const id = `upload-${nanoid()}`
61
+ // eslint-disable-next-line solid/reactivity
62
+ const uploadFileSignal = createSignal<UploadFile>({
63
+ id,
64
+ file,
65
+ name: file.name,
66
+ type: file.type,
67
+ size: file.size,
68
+ status: 'pending',
69
+ percent: 0,
70
+ })
71
+
72
+ const [, setUploadFile] = uploadFileSignal
73
+
74
+ customRequest?.({
75
+ file,
76
+ onProgress(event) {
77
+ setUploadFile(value => ({
78
+ ...value,
79
+ status: 'uploading',
80
+ percent: event.percent ?? 0,
81
+ }))
82
+ },
83
+ onSuccess(response) {
84
+ setUploadFile(value => ({ ...value, status: 'finished', response }))
85
+ },
86
+ onError() {
87
+ setUploadFile(value => ({ ...value, status: 'error' }))
88
+ },
89
+ })
90
+
91
+ return uploadFileSignal
92
+ }
93
+
94
+ const Upload: Component<UploadProps> & {
95
+ request: typeof request
96
+ } = _props => {
97
+ let input: HTMLInputElement | undefined
98
+ const props = mergeProps(
99
+ {
100
+ customRequest: ({ file, onSuccess, onError }) => {
101
+ if (!_props.action) return
102
+
103
+ const formData = new FormData()
104
+ formData.append('file', file)
105
+ fetch(_props.action, {
106
+ method: _props.method ?? 'post',
107
+ body: formData,
108
+ })
109
+ .then(onSuccess)
110
+ .catch(onError)
111
+ },
112
+ } as UploadProps,
113
+ _props,
114
+ )
115
+
116
+ return (
117
+ <span class={props.class} onClick={() => input?.click()}>
118
+ {props.children}
119
+
120
+ <input
121
+ ref={input}
122
+ class="hidden"
123
+ type="file"
124
+ accept={props.accept}
125
+ multiple={props.multiple}
126
+ onInput={e => {
127
+ const fileList: Array<Signal<UploadFile>> = []
128
+ for (const file of e.target.files ?? []) {
129
+ const uploadFileSignal = request(file, props.customRequest)
130
+ fileList.push(uploadFileSignal)
131
+ }
132
+
133
+ props.onAdd?.(fileList)
134
+ e.target.value = ''
135
+ }}
136
+ />
137
+ </span>
138
+ )
139
+ }
140
+
141
+ Upload.request = request
9
142
 
10
143
  export default Upload
package/src/Compact.tsx DELETED
@@ -1,15 +0,0 @@
1
- import { type ParentProps } from 'solid-js'
2
-
3
- interface CompactProps extends ParentProps {}
4
-
5
- function Compact(props: CompactProps) {
6
- return <div class="ant-compact ant-flex">{props.children}</div>
7
- }
8
-
9
- Compact.compactItemClass = 'p[.ant-compact]:ant-ml--1px'
10
- Compact.compactItemRounded0Class = 'p[.ant-compact>*]:ant-rounded-0'
11
- Compact.compactItemRoundedLeftClass = 'p[.ant-compact>:first-child]:ant-rounded-l-6px'
12
- Compact.compactItemRoundedRightClass = 'p[.ant-compact>:last-child]:ant-rounded-r-6px'
13
- Compact.compactItemZIndexClass = 'p[.ant-compact>*]:focus:ant-z-10 p[.ant-compact>*]:focus-within:ant-z-10'
14
-
15
- export default Compact
File without changes