antd-solid 0.0.8 → 0.0.10
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/package.json +4 -3
- package/src/Button.tsx +125 -0
- package/src/Collapse/index.tsx +86 -0
- package/src/ColorPicker.tsx +11 -0
- package/src/Compact.tsx +15 -0
- package/src/DatePicker.tsx +30 -0
- package/src/Empty/PRESENTED_IMAGE_SIMPLE.tsx +15 -0
- package/src/Empty/assets/EmptySvg.tsx +43 -0
- package/src/Empty/assets/SimpleEmptySvg.tsx +16 -0
- package/src/Empty/index.tsx +20 -0
- package/src/Image.tsx +29 -0
- package/src/Input.tsx +202 -0
- package/src/InputNumber.test.tsx +46 -0
- package/src/InputNumber.tsx +125 -0
- package/src/Modal.tsx +196 -0
- package/src/Popconfirm.tsx +75 -0
- package/src/Popover.tsx +30 -0
- package/src/Progress.tsx +4 -0
- package/src/Radio.tsx +132 -0
- package/src/Result.tsx +38 -0
- package/src/Segmented/index.tsx +95 -0
- package/src/Select.tsx +128 -0
- package/src/Skeleton.tsx +14 -0
- package/src/Spin.tsx +23 -0
- package/src/Switch.tsx +34 -0
- package/src/Table.tsx +53 -0
- package/src/Tabs.tsx +131 -0
- package/src/Timeline.tsx +33 -0
- package/src/Tooltip.tsx +340 -0
- package/src/Tree.tsx +246 -0
- package/src/Upload.tsx +10 -0
- package/src/form/Form.tsx +94 -0
- package/src/form/FormItem.tsx +139 -0
- package/src/form/context.ts +16 -0
- package/src/form/index.ts +13 -0
- package/src/hooks/createControllableValue.ts +68 -0
- package/src/hooks/createUpdateEffect.ts +16 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useClickAway.ts +18 -0
- package/src/hooks/useSize.ts +26 -0
- package/src/index.ts +44 -0
- package/src/types/index.ts +5 -0
- package/src/utils/EventEmitter.ts +15 -0
- package/src/utils/ReactToSolid.tsx +38 -0
- package/src/utils/SolidToReact.tsx +27 -0
- package/src/utils/array.ts +21 -0
- package/src/utils/component.tsx +85 -0
- package/src/utils/solid.ts +53 -0
- package/src/utils/zh_CN.ts +236 -0
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "antd-solid",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "src/index.ts",
|
|
6
6
|
"types": "es/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"devDependencies": {
|
|
@@ -68,7 +68,8 @@
|
|
|
68
68
|
"files": [
|
|
69
69
|
"dist",
|
|
70
70
|
"es",
|
|
71
|
-
"css"
|
|
71
|
+
"css",
|
|
72
|
+
"src"
|
|
72
73
|
],
|
|
73
74
|
"scripts": {
|
|
74
75
|
"docs:dev": "vitepress dev docs",
|
package/src/Button.tsx
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Component,
|
|
3
|
+
mergeProps,
|
|
4
|
+
type ParentProps,
|
|
5
|
+
type JSX,
|
|
6
|
+
Show,
|
|
7
|
+
createSignal,
|
|
8
|
+
createMemo,
|
|
9
|
+
} from 'solid-js'
|
|
10
|
+
import cs from 'classnames'
|
|
11
|
+
|
|
12
|
+
interface ButtonProps extends ParentProps, JSX.CustomAttributes<HTMLButtonElement> {
|
|
13
|
+
type?: 'default' | 'primary' | 'dashed' | 'text' | 'link'
|
|
14
|
+
onClick?: ((e: MouseEvent) => void) | ((e: MouseEvent) => Promise<unknown>)
|
|
15
|
+
/**
|
|
16
|
+
* 默认: middle
|
|
17
|
+
* plain: 没有多余的 padding 和高度等
|
|
18
|
+
*/
|
|
19
|
+
size?: 'large' | 'middle' | 'small' | 'plain'
|
|
20
|
+
class?: string
|
|
21
|
+
style?: JSX.CSSProperties
|
|
22
|
+
loading?: boolean
|
|
23
|
+
/**
|
|
24
|
+
* 设置危险按钮
|
|
25
|
+
*/
|
|
26
|
+
danger?: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const sizeClassMap = {
|
|
30
|
+
large: 'ant-px-15px ant-py-6px ant-h-40px ant-rounded-8px',
|
|
31
|
+
middle: 'ant-px-15px ant-py-4px ant-h-32px ant-rounded-6px',
|
|
32
|
+
small: 'ant-px-7px ant-h-24px ant-rounded-4px',
|
|
33
|
+
plain: 'ant-p-0',
|
|
34
|
+
} as const
|
|
35
|
+
|
|
36
|
+
const typeClassMap = {
|
|
37
|
+
default: (danger: boolean) =>
|
|
38
|
+
cs(
|
|
39
|
+
'ant-bg-white',
|
|
40
|
+
danger
|
|
41
|
+
? 'ant-[border:1px_solid_var(--ant-color-error)] ant-text-[var(--ant-color-error)] hover:ant-[border-color:var(--light-error-color)] hover:ant-text-[var(--light-error-color)] active:ant-[border-color:var(--dark-error-color)] active:ant-text-[var(--dark-error-color)]'
|
|
42
|
+
: 'ant-[border:1px_solid_var(--ant-color-border)] ant-text-[var(--dark-color)] hover:ant-[border-color:var(--light-primary-color)] hover:ant-text-[var(--light-primary-color)] active:ant-[border-color:var(--dark-primary-color)] active:ant-text-[var(--dark-primary-color)]',
|
|
43
|
+
),
|
|
44
|
+
primary: (danger: boolean) =>
|
|
45
|
+
cs(
|
|
46
|
+
'ant-border-none ant-text-white',
|
|
47
|
+
danger
|
|
48
|
+
? 'ant-bg-[var(--ant-color-error)] hover:ant-bg-[var(--light-error-color)] active:ant-bg-[var(--dark-error-color)]'
|
|
49
|
+
: 'ant-bg-[var(--primary-color)] hover:ant-bg-[var(--light-primary-color)] active:ant-bg-[var(--dark-primary-color)]',
|
|
50
|
+
),
|
|
51
|
+
dashed: (danger: boolean) =>
|
|
52
|
+
cs(
|
|
53
|
+
' ant-bg-white',
|
|
54
|
+
danger
|
|
55
|
+
? 'ant-[border:1px_dashed_var(--ant-color-error)] ant-text-[var(--ant-color-error)] hover:ant-[border-color:var(--light-error-color)] hover:ant-text-[var(--light-error-color)] active:ant-[border-color:var(--dark-error-color)] active:ant-text-[var(--dark-error-color)]'
|
|
56
|
+
: 'ant-[border:1px_dashed_var(--ant-color-border)] ant-text-[var(--dark-color)] hover:ant-[border-color:var(--light-primary-color)] hover:ant-text-[var(--light-primary-color)] active:ant-[border-color:var(--dark-primary-color)] active:ant-text-[var(--dark-primary-color)]',
|
|
57
|
+
),
|
|
58
|
+
text: (danger: boolean) =>
|
|
59
|
+
cs(
|
|
60
|
+
'ant-border-none ant-bg-transparent',
|
|
61
|
+
danger
|
|
62
|
+
? 'ant-text-[var(--ant-color-error)] hover:ant-bg-[var(--error-bg-color)] active:ant-bg-[var(--error-bg-color)]'
|
|
63
|
+
: 'ant-text-[var(--dark-color)] hover:ant-bg-[rgba(0,0,0,0.06)] active:ant-bg-[rgba(0,0,0,.15)]',
|
|
64
|
+
),
|
|
65
|
+
link: (danger: boolean) =>
|
|
66
|
+
cs(
|
|
67
|
+
'ant-border-none ant-bg-transparent',
|
|
68
|
+
danger
|
|
69
|
+
? 'ant-text-[var(--ant-color-error)] hover:ant-text-[var(--light-error-color)] active:ant-text-[var(--dark-error-color)]'
|
|
70
|
+
: 'ant-text-[var(--primary-color)] hover:ant-text-[var(--light-primary-color)] active:ant-text-[var(--dark-primary-color)]',
|
|
71
|
+
),
|
|
72
|
+
} as const
|
|
73
|
+
|
|
74
|
+
const Button: Component<ButtonProps> = props => {
|
|
75
|
+
const mergedProps = mergeProps({ type: 'default', size: 'middle' } as ButtonProps, props)
|
|
76
|
+
const [innerLoading, setLoading] = createSignal(false)
|
|
77
|
+
const loading = createMemo(() => props.loading ?? innerLoading())
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<button
|
|
81
|
+
ref={mergedProps.ref}
|
|
82
|
+
class={cs(
|
|
83
|
+
'ant-relative ant-cursor-pointer',
|
|
84
|
+
mergedProps.class,
|
|
85
|
+
sizeClassMap[mergedProps.size!],
|
|
86
|
+
typeClassMap[mergedProps.type!](props.danger ?? false),
|
|
87
|
+
loading() && 'ant-opacity-65',
|
|
88
|
+
'ant-[--color:--light-primary-color]'
|
|
89
|
+
)}
|
|
90
|
+
style={mergedProps.style}
|
|
91
|
+
onClick={e => {
|
|
92
|
+
const res = mergedProps.onClick?.(e)
|
|
93
|
+
if (res instanceof Promise) {
|
|
94
|
+
setLoading(true)
|
|
95
|
+
res.finally(() => setLoading(false))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (
|
|
99
|
+
mergedProps.type === 'default' ||
|
|
100
|
+
mergedProps.type === 'primary' ||
|
|
101
|
+
mergedProps.type === 'dashed'
|
|
102
|
+
) {
|
|
103
|
+
const div = document.createElement('div')
|
|
104
|
+
div.className = cs(
|
|
105
|
+
props.danger ? 'ant-[--color:var(--light-error-color)]' : 'ant-[--color:var(--light-primary-color)]',
|
|
106
|
+
'ant-absolute ant-inset-0 ant-rounded-inherit ant-[background:radial-gradient(var(--color),rgba(0,0,0,0))] ant-z--1 ant-keyframes-button-border[inset:0px][inset:-6px] ant-[animation:button-border_ease-out_0.3s]',
|
|
107
|
+
)
|
|
108
|
+
const onAnimationEnd = () => {
|
|
109
|
+
div.remove()
|
|
110
|
+
div.removeEventListener('animationend', onAnimationEnd)
|
|
111
|
+
}
|
|
112
|
+
div.addEventListener('animationend', onAnimationEnd)
|
|
113
|
+
e.currentTarget.insertBefore(div, e.currentTarget.childNodes[0])
|
|
114
|
+
}
|
|
115
|
+
}}
|
|
116
|
+
>
|
|
117
|
+
<Show when={loading()}>
|
|
118
|
+
<span class="i-ant-design:loading ant-[vertical-align:-0.125em] keyframes-spin ant-[animation:spin_1s_linear_infinite] ant-mr-8px" />
|
|
119
|
+
</Show>
|
|
120
|
+
<span>{mergedProps.children}</span>
|
|
121
|
+
</button>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default Button
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { type JSX, type JSXElement, type Component, For, Show } from 'solid-js'
|
|
2
|
+
import cs from 'classnames'
|
|
3
|
+
import { Transition } from 'solid-transition-group'
|
|
4
|
+
import { type Key } from '../types'
|
|
5
|
+
import createControllableValue from '../hooks/createControllableValue'
|
|
6
|
+
|
|
7
|
+
export interface CollapseItem {
|
|
8
|
+
key: Key
|
|
9
|
+
label: JSXElement
|
|
10
|
+
children: JSXElement
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CollapseProps {
|
|
14
|
+
class?: string
|
|
15
|
+
defaultActiveKey?: Key[]
|
|
16
|
+
activeKey?: Key[]
|
|
17
|
+
items: CollapseItem[]
|
|
18
|
+
style?: JSX.CSSProperties
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const Collapse: Component<CollapseProps> = props => {
|
|
22
|
+
const [activeKey, setActiveKey] = createControllableValue<Key[]>(props, {
|
|
23
|
+
defaultValuePropName: 'defaultActiveKey',
|
|
24
|
+
valuePropName: 'activeKey',
|
|
25
|
+
defaultValue: [],
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div
|
|
30
|
+
class={cs(
|
|
31
|
+
'ant-rounded-[var(--ant-border-radius-lg)] ant-[border:1px_solid_var(--ant-color-border)] ant-border-b-0',
|
|
32
|
+
props.class,
|
|
33
|
+
)}
|
|
34
|
+
style={props.style}
|
|
35
|
+
>
|
|
36
|
+
<For each={props.items}>
|
|
37
|
+
{item => (
|
|
38
|
+
<div class="ant-[border-bottom:1px_solid_var(--ant-color-border)] first:ant-rounded-t-[var(--ant-border-radius-lg)] last:ant-rounded-b-[var(--ant-border-radius-lg)] ant-cursor-pointer">
|
|
39
|
+
<div
|
|
40
|
+
class="ant-bg-[var(--ant-collapse-header-bg)] ant-text-[var(--ant-color-text-heading)] ant-p-[var(--ant-collapse-header-padding)]"
|
|
41
|
+
onClick={() => {
|
|
42
|
+
setActiveKey(keys => {
|
|
43
|
+
if (keys.includes(item.key)) {
|
|
44
|
+
return keys.filter(key => key !== item.key)
|
|
45
|
+
}
|
|
46
|
+
return [...keys, item.key]
|
|
47
|
+
})
|
|
48
|
+
}}
|
|
49
|
+
>
|
|
50
|
+
<span
|
|
51
|
+
class={cs(
|
|
52
|
+
'i-ant-design:right-outlined',
|
|
53
|
+
'ant-mr-[var(--ant-margin-sm)] ant-duration-.3s',
|
|
54
|
+
activeKey().includes(item.key) && 'ant-rotate-[90deg]',
|
|
55
|
+
)}
|
|
56
|
+
/>
|
|
57
|
+
{item.label}
|
|
58
|
+
</div>
|
|
59
|
+
<Transition
|
|
60
|
+
onEnter={(el, done) => {
|
|
61
|
+
el.animate([{ height: '0px' }, { height: `${el.scrollHeight}px` }], {
|
|
62
|
+
duration: 300,
|
|
63
|
+
}).finished.finally(done)
|
|
64
|
+
}}
|
|
65
|
+
onExit={(el, done) => {
|
|
66
|
+
el.animate([{ height: `${el.scrollHeight}px` }, { height: '0px' }], {
|
|
67
|
+
duration: 300,
|
|
68
|
+
}).finished.finally(done)
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
<Show when={activeKey().includes(item.key)}>
|
|
72
|
+
<div class="ant-overflow-hidden">
|
|
73
|
+
<div class="ant-p-[var(--ant-padding-sm)] ant-[border-top:1px_solid_var(--ant-color-border)]">
|
|
74
|
+
{item.children}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</Show>
|
|
78
|
+
</Transition>
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
81
|
+
</For>
|
|
82
|
+
</div>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export default Collapse
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ColorPicker } from 'antd'
|
|
2
|
+
import { type Color } from 'antd/es/color-picker'
|
|
3
|
+
import { reactToSolidComponent } from './utils/component'
|
|
4
|
+
|
|
5
|
+
export interface ColorPickerProps {
|
|
6
|
+
defaultValue?: string | Color
|
|
7
|
+
value?: string | Color
|
|
8
|
+
onChange?: (value: Color, hex: string) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default reactToSolidComponent(ColorPicker)
|
package/src/Compact.tsx
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
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
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { DatePicker as DatePickerAntd } from 'antd'
|
|
2
|
+
import { type DatePickerProps, type RangePickerProps } from 'antd/es/date-picker'
|
|
3
|
+
import { reactToSolidComponent, replaceClassName } from './utils/component'
|
|
4
|
+
|
|
5
|
+
const RangePicker = replaceClassName(
|
|
6
|
+
reactToSolidComponent<
|
|
7
|
+
RangePickerProps & {
|
|
8
|
+
dropdownClassName?: string | undefined
|
|
9
|
+
popupClassName?: string | undefined
|
|
10
|
+
rootClassName?: string | undefined
|
|
11
|
+
}
|
|
12
|
+
>(DatePickerAntd.RangePicker),
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
const _DatePicker = replaceClassName(
|
|
16
|
+
reactToSolidComponent<
|
|
17
|
+
DatePickerProps & {
|
|
18
|
+
status?: '' | 'error' | 'warning' | undefined
|
|
19
|
+
hashId?: string | undefined
|
|
20
|
+
popupClassName?: string | undefined
|
|
21
|
+
rootClassName?: string | undefined
|
|
22
|
+
}
|
|
23
|
+
>(DatePickerAntd),
|
|
24
|
+
)
|
|
25
|
+
const DatePicker = _DatePicker as typeof _DatePicker & {
|
|
26
|
+
RangePicker: typeof RangePicker
|
|
27
|
+
}
|
|
28
|
+
DatePicker.RangePicker = RangePicker
|
|
29
|
+
|
|
30
|
+
export default DatePicker
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Component } from 'solid-js'
|
|
2
|
+
import SimpleEmptySvg from './assets/SimpleEmptySvg'
|
|
3
|
+
|
|
4
|
+
const PRESENTED_IMAGE_SIMPLE: Component = () => {
|
|
5
|
+
return (
|
|
6
|
+
<div class='ant-my-[var(--ant-margin-xl)] ant-mx-[var(--ant-margin-xs)]'>
|
|
7
|
+
<div class="ant-mb-[var(--ant-margin-xs)] ant-flex ant-justify-center">
|
|
8
|
+
<SimpleEmptySvg />
|
|
9
|
+
</div>
|
|
10
|
+
<div class="ant-text-[var(--ant-color-text-disabled)] ant-text-center">暂无数据</div>
|
|
11
|
+
</div>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default PRESENTED_IMAGE_SIMPLE
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const EmptySvg = () => (
|
|
2
|
+
<svg width="184" height="100" viewBox="0 0 184 152" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<g fill="none" fill-rule="evenodd">
|
|
4
|
+
<g transform="translate(24 31.67)">
|
|
5
|
+
<ellipse
|
|
6
|
+
fill-opacity=".8"
|
|
7
|
+
fill="#F5F5F7"
|
|
8
|
+
cx="67.797"
|
|
9
|
+
cy="106.89"
|
|
10
|
+
rx="67.797"
|
|
11
|
+
ry="12.668"
|
|
12
|
+
/>
|
|
13
|
+
<path
|
|
14
|
+
d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z"
|
|
15
|
+
fill="#AEB8C2"
|
|
16
|
+
/>
|
|
17
|
+
<path
|
|
18
|
+
d="M101.537 86.214L80.63 61.102c-1.001-1.207-2.507-1.867-4.048-1.867H31.724c-1.54 0-3.047.66-4.048 1.867L6.769 86.214v13.792h94.768V86.214z"
|
|
19
|
+
fill="url(#linearGradient-1)"
|
|
20
|
+
transform="translate(13.56)"
|
|
21
|
+
/>
|
|
22
|
+
<path
|
|
23
|
+
d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"
|
|
24
|
+
fill="#F5F5F7"
|
|
25
|
+
/>
|
|
26
|
+
<path
|
|
27
|
+
d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z"
|
|
28
|
+
fill="#DCE0E6"
|
|
29
|
+
/>
|
|
30
|
+
</g>
|
|
31
|
+
<path
|
|
32
|
+
d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z"
|
|
33
|
+
fill="#DCE0E6"
|
|
34
|
+
/>
|
|
35
|
+
<g transform="translate(149.65 15.383)" fill="#FFF">
|
|
36
|
+
<ellipse cx="20.654" cy="3.167" rx="2.849" ry="2.815" />
|
|
37
|
+
<path d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z" />
|
|
38
|
+
</g>
|
|
39
|
+
</g>
|
|
40
|
+
</svg>
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
export default EmptySvg
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const EmptySvg = () => (
|
|
2
|
+
<svg width="64" height="41" viewBox="0 0 64 41" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<g transform="translate(0 1)" fill="none" fill-rule="evenodd">
|
|
4
|
+
<ellipse fill="#f5f5f5" cx="32" cy="33" rx="32" ry="7" />
|
|
5
|
+
<g fill-rule="nonzero" stroke="#d9d9d9">
|
|
6
|
+
<path d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z" />
|
|
7
|
+
<path
|
|
8
|
+
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
|
|
9
|
+
fill="#fafafa"
|
|
10
|
+
/>
|
|
11
|
+
</g>
|
|
12
|
+
</g>
|
|
13
|
+
</svg>
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
export default EmptySvg
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Component } from 'solid-js'
|
|
2
|
+
import PRESENTED_IMAGE_SIMPLE from './PRESENTED_IMAGE_SIMPLE'
|
|
3
|
+
import EmptySvg from './assets/EmptySvg'
|
|
4
|
+
|
|
5
|
+
const Empty: Component & {
|
|
6
|
+
PRESENTED_IMAGE_SIMPLE: Component
|
|
7
|
+
} = () => {
|
|
8
|
+
return (
|
|
9
|
+
<div>
|
|
10
|
+
<div class='ant-mb-[var(--ant-margin-xs)] ant-flex ant-justify-center'>
|
|
11
|
+
<EmptySvg />
|
|
12
|
+
</div>
|
|
13
|
+
<div class='ant-text-[var(--ant-color-text)] ant-text-center'>暂无数据</div>
|
|
14
|
+
</div>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
Empty.PRESENTED_IMAGE_SIMPLE = PRESENTED_IMAGE_SIMPLE
|
|
19
|
+
|
|
20
|
+
export default Empty
|
package/src/Image.tsx
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Image as ImageAntd } from 'antd'
|
|
2
|
+
import { configProvider, reactToSolidComponent, replaceClassName } from './utils/component'
|
|
3
|
+
import { solidToReact } from './utils/solid'
|
|
4
|
+
import { type JSXElement, createMemo } from 'solid-js'
|
|
5
|
+
import { mapValues } from 'lodash-es'
|
|
6
|
+
|
|
7
|
+
const _Image = replaceClassName(
|
|
8
|
+
reactToSolidComponent(configProvider(ImageAntd), () => (<div class="ant-inline-flex" />) as any),
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
type ImageProps = Omit<Parameters<typeof _Image>[0], 'placeholder'> & {
|
|
12
|
+
placeholder?: JSXElement
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function Image(_props: ImageProps) {
|
|
16
|
+
const props = createMemo(() =>
|
|
17
|
+
mapValues(_props, (value, key) => {
|
|
18
|
+
switch (key) {
|
|
19
|
+
case 'placeholder':
|
|
20
|
+
return solidToReact(value)
|
|
21
|
+
default:
|
|
22
|
+
return value
|
|
23
|
+
}
|
|
24
|
+
}),
|
|
25
|
+
)
|
|
26
|
+
return <_Image {...(props() as any)} />
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default Image
|
package/src/Input.tsx
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { isNil, omit } from 'lodash-es'
|
|
2
|
+
import { Show, createMemo, splitProps } from 'solid-js'
|
|
3
|
+
import type { JSX, JSXElement, Component } from 'solid-js'
|
|
4
|
+
import cs from 'classnames'
|
|
5
|
+
import createControllableValue from './hooks/createControllableValue'
|
|
6
|
+
import { Dynamic } from 'solid-js/web'
|
|
7
|
+
import Compact from './Compact'
|
|
8
|
+
|
|
9
|
+
type CommonInputProps<T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> =
|
|
10
|
+
JSX.CustomAttributes<T> & {
|
|
11
|
+
textarea?: boolean
|
|
12
|
+
defaultValue?: string | undefined
|
|
13
|
+
value?: string | undefined
|
|
14
|
+
addonBefore?: JSXElement
|
|
15
|
+
addonAfter?: JSXElement
|
|
16
|
+
prefix?: JSXElement
|
|
17
|
+
suffix?: JSXElement
|
|
18
|
+
placeholder?: string
|
|
19
|
+
/**
|
|
20
|
+
* 仅供 InputNumber 使用
|
|
21
|
+
*/
|
|
22
|
+
actions?: JSXElement
|
|
23
|
+
/**
|
|
24
|
+
* 设置校验状态
|
|
25
|
+
*/
|
|
26
|
+
status?: 'error' | 'warning'
|
|
27
|
+
onChange?: JSX.InputEventHandler<T, InputEvent>
|
|
28
|
+
onPressEnter?: JSX.EventHandler<T, KeyboardEvent>
|
|
29
|
+
onKeyDown?: JSX.EventHandler<T, KeyboardEvent>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const statusClassDict = {
|
|
33
|
+
default: (disabled: boolean) =>
|
|
34
|
+
cs(
|
|
35
|
+
'ant-[border:1px_solid_var(--ant-color-border)]',
|
|
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)]',
|
|
38
|
+
),
|
|
39
|
+
error: (disabled: boolean) =>
|
|
40
|
+
cs(
|
|
41
|
+
'ant-[border:1px_solid_var(--ant-color-error)]',
|
|
42
|
+
!disabled &&
|
|
43
|
+
'hover:ant-border-[var(--light-error-color)] focus-within:ant-[box-shadow:0_0_0_2px_rgba(255,38,5,.06)]',
|
|
44
|
+
),
|
|
45
|
+
warning: (disabled: boolean) =>
|
|
46
|
+
cs(
|
|
47
|
+
'ant-[border:1px_solid_var(--warning-color)]',
|
|
48
|
+
!disabled &&
|
|
49
|
+
'hover:ant-border-[var(--color-warning-border-hover)] focus-within:ant-[box-shadow:0_0_0_2px_rgba(255,215,5,.1)]',
|
|
50
|
+
),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function CommonInput<T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement>(
|
|
54
|
+
props: CommonInputProps<T> &
|
|
55
|
+
Omit<JSX.InputHTMLAttributes<T>, 'onChange' | 'onInput' | 'onKeyDown'>,
|
|
56
|
+
) {
|
|
57
|
+
const [{ style, onChange, onPressEnter, onKeyDown }, inputProps] = splitProps(props, [
|
|
58
|
+
'defaultValue',
|
|
59
|
+
'value',
|
|
60
|
+
'class',
|
|
61
|
+
'addonBefore',
|
|
62
|
+
'addonAfter',
|
|
63
|
+
'suffix',
|
|
64
|
+
'onChange',
|
|
65
|
+
'onPressEnter',
|
|
66
|
+
'onKeyDown',
|
|
67
|
+
'actions',
|
|
68
|
+
'style',
|
|
69
|
+
])
|
|
70
|
+
|
|
71
|
+
const [_, controllableProps] = splitProps(props, ['onChange'])
|
|
72
|
+
const [value, setValue] = createControllableValue(controllableProps)
|
|
73
|
+
|
|
74
|
+
const inputWrapClass = createMemo(() =>
|
|
75
|
+
cs(
|
|
76
|
+
'ant-px-11px ant-py-4px ant-rounded-6px',
|
|
77
|
+
!props.textarea && 'ant-h-32px',
|
|
78
|
+
props.addonBefore ? 'ant-rounded-l-0' : Compact.compactItemRoundedLeftClass,
|
|
79
|
+
props.addonAfter ? 'ant-rounded-r-0' : Compact.compactItemRoundedRightClass,
|
|
80
|
+
statusClassDict[props.status ?? 'default'](!!inputProps.disabled),
|
|
81
|
+
Compact.compactItemRounded0Class,
|
|
82
|
+
Compact.compactItemZIndexClass,
|
|
83
|
+
),
|
|
84
|
+
)
|
|
85
|
+
const hasPrefixOrSuffix = createMemo(
|
|
86
|
+
() => !isNil(props.prefix) || !isNil(props.suffix) || !isNil(props.actions),
|
|
87
|
+
)
|
|
88
|
+
const inputJSX = (
|
|
89
|
+
<Dynamic<Component<JSX.InputHTMLAttributes<HTMLInputElement>>>
|
|
90
|
+
component={
|
|
91
|
+
(props.textarea ? 'textarea' : 'input') as unknown as Component<
|
|
92
|
+
JSX.InputHTMLAttributes<HTMLInputElement>
|
|
93
|
+
>
|
|
94
|
+
}
|
|
95
|
+
{...(inputProps as JSX.InputHTMLAttributes<HTMLInputElement>)}
|
|
96
|
+
class={cs(
|
|
97
|
+
'ant-w-full ant-[outline:none] ant-text-14px',
|
|
98
|
+
!hasPrefixOrSuffix() && inputWrapClass(),
|
|
99
|
+
inputProps.disabled &&
|
|
100
|
+
'ant-bg-[var(--ant-color-bg-container-disabled)] ant-cursor-not-allowed',
|
|
101
|
+
)}
|
|
102
|
+
value={value() ?? ''}
|
|
103
|
+
onInput={e => {
|
|
104
|
+
setValue(e.target.value)
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
106
|
+
onChange?.(e as any)
|
|
107
|
+
|
|
108
|
+
if (Object.keys(props).includes('value')) {
|
|
109
|
+
e.target.value = value()
|
|
110
|
+
}
|
|
111
|
+
}}
|
|
112
|
+
onKeyDown={e => {
|
|
113
|
+
if (e.key === 'Enter') {
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
115
|
+
onPressEnter?.(e as any)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
119
|
+
onKeyDown?.(e as any)
|
|
120
|
+
}}
|
|
121
|
+
/>
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<div class={cs('ant-flex ant-w-full', Compact.compactItemClass)} style={style}>
|
|
126
|
+
<Show when={props.addonBefore}>
|
|
127
|
+
<div
|
|
128
|
+
class={cs(
|
|
129
|
+
'ant-shrink-0 ant-flex ant-justify-center ant-items-center ant-px-11px ant-bg-[rgba(0,0,0,.02)] ant-[border:1px_solid_var(--ant-color-border)] ant-border-r-0 ant-rounded-l-6px ant-text-14px',
|
|
130
|
+
Compact.compactItemRounded0Class,
|
|
131
|
+
Compact.compactItemRoundedLeftClass,
|
|
132
|
+
)}
|
|
133
|
+
>
|
|
134
|
+
{props.addonBefore}
|
|
135
|
+
</div>
|
|
136
|
+
</Show>
|
|
137
|
+
|
|
138
|
+
<Show when={hasPrefixOrSuffix()} fallback={inputJSX}>
|
|
139
|
+
<div
|
|
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)]',
|
|
142
|
+
inputWrapClass(),
|
|
143
|
+
)}
|
|
144
|
+
>
|
|
145
|
+
<Show when={props.prefix}>
|
|
146
|
+
<div class="ant-mr-4px">{props.prefix}</div>
|
|
147
|
+
</Show>
|
|
148
|
+
|
|
149
|
+
{inputJSX}
|
|
150
|
+
|
|
151
|
+
<Show when={props.suffix}>
|
|
152
|
+
<div class="ant-ml-4px">{props.suffix}</div>
|
|
153
|
+
</Show>
|
|
154
|
+
|
|
155
|
+
<Show when={props.actions}>
|
|
156
|
+
<div class="ant-[display:var(--input-after-display)] ant-absolute ant-top-0 ant-bottom-0 ant-right-0 ant-h-[calc(100%-2px)] ant-translate-y-1px -ant-translate-x-1px">
|
|
157
|
+
{props.actions}
|
|
158
|
+
</div>
|
|
159
|
+
</Show>
|
|
160
|
+
</div>
|
|
161
|
+
</Show>
|
|
162
|
+
|
|
163
|
+
<Show when={props.addonAfter}>
|
|
164
|
+
<div
|
|
165
|
+
class={cs(
|
|
166
|
+
'ant-shrink-0 ant-flex ant-justify-center ant-items-center ant-px-11px ant-bg-[rgba(0,0,0,.02)] ant-[border:1px_solid_var(--ant-color-border)] ant-border-l-0 ant-rounded-r-6px ant-text-14px',
|
|
167
|
+
Compact.compactItemRounded0Class,
|
|
168
|
+
Compact.compactItemRoundedRightClass,
|
|
169
|
+
)}
|
|
170
|
+
>
|
|
171
|
+
{props.addonAfter}
|
|
172
|
+
</div>
|
|
173
|
+
</Show>
|
|
174
|
+
</div>
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export type InputProps = Omit<CommonInputProps, 'actions' | 'textarea'> &
|
|
179
|
+
Omit<JSX.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'onInput' | 'onKeyDown'>
|
|
180
|
+
|
|
181
|
+
export type TextAreaProps = Omit<
|
|
182
|
+
CommonInputProps<HTMLTextAreaElement>,
|
|
183
|
+
'prefix' | 'suffix' | 'textarea'
|
|
184
|
+
> &
|
|
185
|
+
Omit<
|
|
186
|
+
JSX.TextareaHTMLAttributes<HTMLTextAreaElement>,
|
|
187
|
+
'actions' | 'onChange' | 'onInput' | 'onKeyDown'
|
|
188
|
+
>
|
|
189
|
+
|
|
190
|
+
const Input: Component<InputProps> & {
|
|
191
|
+
TextArea: Component<TextAreaProps>
|
|
192
|
+
} = props => {
|
|
193
|
+
return <CommonInput {...omit(props, ['actions'])} />
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
Input.TextArea = props => {
|
|
197
|
+
return (
|
|
198
|
+
<CommonInput<HTMLTextAreaElement> textarea {...omit(props, ['prefix', 'suffix', 'actions'])} />
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export default Input
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { fireEvent, render } from '@solidjs/testing-library'
|
|
3
|
+
import InputNumber from './InputNumber'
|
|
4
|
+
import '@testing-library/jest-dom'
|
|
5
|
+
|
|
6
|
+
describe('InputNumber component', () => {
|
|
7
|
+
it('onChange', () => {
|
|
8
|
+
const onChange = vi.fn()
|
|
9
|
+
const { getByPlaceholderText } = render(() => (
|
|
10
|
+
<InputNumber placeholder="input-number" onChange={onChange} />
|
|
11
|
+
))
|
|
12
|
+
|
|
13
|
+
const input: HTMLInputElement = getByPlaceholderText('input-number')
|
|
14
|
+
input.value = '123'
|
|
15
|
+
fireEvent.input(input)
|
|
16
|
+
expect(onChange).toHaveBeenLastCalledWith(123)
|
|
17
|
+
|
|
18
|
+
input.value = '1234'
|
|
19
|
+
fireEvent.input(input)
|
|
20
|
+
expect(onChange).toBeCalledTimes(2)
|
|
21
|
+
expect(onChange).toHaveBeenLastCalledWith(1234)
|
|
22
|
+
|
|
23
|
+
input.value = '1234'
|
|
24
|
+
fireEvent.input(input)
|
|
25
|
+
expect(onChange).toBeCalledTimes(2)
|
|
26
|
+
|
|
27
|
+
input.value = '1234.'
|
|
28
|
+
fireEvent.input(input)
|
|
29
|
+
expect(onChange).toBeCalledTimes(2)
|
|
30
|
+
|
|
31
|
+
input.value = '1234.0'
|
|
32
|
+
fireEvent.input(input)
|
|
33
|
+
expect(onChange).toBeCalledTimes(2)
|
|
34
|
+
|
|
35
|
+
input.value = '1234.01'
|
|
36
|
+
fireEvent.input(input)
|
|
37
|
+
expect(onChange).toBeCalledTimes(3)
|
|
38
|
+
|
|
39
|
+
input.value = '123x'
|
|
40
|
+
fireEvent.input(input)
|
|
41
|
+
expect(onChange).toBeCalledTimes(3)
|
|
42
|
+
|
|
43
|
+
fireEvent.blur(input)
|
|
44
|
+
expect(onChange).toBeCalledTimes(3)
|
|
45
|
+
})
|
|
46
|
+
})
|