antd-solid 0.0.2 → 0.0.3
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/.eslintignore +2 -0
- package/.husky/pre-commit +4 -0
- package/.vscode/settings.json +1 -1
- package/docs/.vitepress/components/Code.vue +127 -11
- package/docs/.vitepress/config.ts +39 -1
- package/docs/.vitepress/theme/index.css +4 -0
- package/docs/.vitepress/theme/index.ts +0 -1
- package/docs/components/{Button.tsx → button/Base.tsx} +1 -0
- package/docs/components/button/Danger.tsx +21 -0
- package/docs/components/button.md +19 -7
- package/docs/components/collapse/Base.tsx +25 -0
- package/docs/components/collapse.md +26 -0
- package/docs/components/color-picker/Base.tsx +14 -0
- package/docs/components/color-picker.md +26 -0
- package/docs/components/compact/Base.tsx +27 -0
- package/docs/components/compact.md +26 -0
- package/docs/components/date-picker/Base.tsx +15 -0
- package/docs/components/date-picker.md +26 -0
- package/docs/components/empty/Base.tsx +8 -0
- package/docs/components/empty/PRESENTED_IMAGE_SIMPLE.tsx +8 -0
- package/docs/components/empty.md +32 -0
- package/docs/components/form/Base.tsx +63 -0
- package/docs/components/form.md +27 -0
- package/docs/components/image/Base.tsx +13 -0
- package/docs/components/image.md +26 -0
- package/docs/components/input/AddonBeforeAndAfter.tsx +14 -0
- package/docs/components/input/Base.tsx +15 -0
- package/docs/components/input/Disabled.tsx +19 -0
- package/docs/components/input/PrefixAndSuffix.tsx +14 -0
- package/docs/components/input/Status.tsx +25 -0
- package/docs/components/input/TextArea.tsx +14 -0
- package/docs/components/input-number/Base.tsx +15 -0
- package/docs/components/input-number/MinMax.tsx +17 -0
- package/docs/components/input-number.md +34 -0
- package/docs/components/input.md +57 -0
- package/docs/components/modal/Base.tsx +35 -0
- package/docs/components/modal.md +26 -0
- package/docs/components/popconfirm/Base.tsx +25 -0
- package/docs/components/popconfirm.md +26 -0
- package/docs/components/popover/Base.tsx +19 -0
- package/docs/components/popover.md +26 -0
- package/docs/components/progress/Base.tsx +16 -0
- package/docs/components/progress.md +26 -0
- package/docs/components/radio/Base.tsx +10 -0
- package/docs/components/radio.md +26 -0
- package/docs/components/segmented/Base.tsx +10 -0
- package/docs/components/segmented/Block.tsx +10 -0
- package/docs/components/segmented/Disabled.tsx +28 -0
- package/docs/components/segmented.md +38 -0
- package/docs/components/select/AllowClear.tsx +18 -0
- package/docs/components/select/Base.tsx +17 -0
- package/docs/components/select.md +35 -0
- package/docs/components/switch/Base.tsx +14 -0
- package/docs/components/switch.md +26 -0
- package/docs/components/{Table.tsx → table/Base.tsx} +2 -2
- package/docs/components/table.md +12 -6
- package/docs/components/tabs/Base.tsx +25 -0
- package/docs/components/tabs/Segment.tsx +25 -0
- package/docs/components/tabs.md +33 -0
- package/docs/components/tooltip/Base.tsx +12 -0
- package/docs/components/tooltip/Placement.tsx +90 -0
- package/docs/components/tooltip.md +32 -0
- package/docs/components/tree/Base.tsx +45 -0
- package/docs/components/tree.md +26 -0
- package/global.d.ts +9 -0
- package/package.json +29 -14
- package/patches/vitepress@1.0.0-rc.4.patch +60 -0
- package/rollup.config.js +60 -20
- package/scripts/annotationNonProductionCode.js +30 -0
- package/scripts/cancelAnnotationNonProductionCode.js +30 -0
- package/src/Button.tsx +51 -12
- package/src/Collapse/index.tsx +86 -0
- package/src/ColorPicker.tsx +6 -61
- package/src/Compact.tsx +15 -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/Input.tsx +154 -62
- package/src/InputNumber.tsx +68 -62
- package/src/Modal.tsx +90 -62
- package/src/Popconfirm.tsx +5 -3
- package/src/Segmented/index.tsx +95 -0
- package/src/Select.tsx +125 -3
- package/src/Switch.tsx +1 -1
- package/src/Table.tsx +36 -29
- package/src/Tabs.tsx +88 -45
- package/src/Tooltip.tsx +233 -87
- package/src/Tree.tsx +4 -4
- 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 +9 -6
- package/src/index.ts +12 -5
- package/src/types/index.ts +5 -0
- package/src/utils/EventEmitter.ts +15 -0
- package/src/utils/component.tsx +1 -1
- package/src/utils/solid.ts +8 -3
- package/src/utils/zh_CN.ts +236 -0
- package/tsconfig.json +6 -4
- package/unocss.config.ts +90 -6
- package/src/Button.css +0 -14
- package/src/Form.tsx +0 -98
- package/src/index.css +0 -21
package/src/InputNumber.tsx
CHANGED
|
@@ -1,91 +1,87 @@
|
|
|
1
|
-
import { type Component, createEffect, on, splitProps } from 'solid-js'
|
|
1
|
+
import { type Component, createEffect, on, splitProps, untrack, createSignal, mergeProps } from 'solid-js'
|
|
2
2
|
import { CommonInput, type InputProps } from './Input'
|
|
3
|
-
import { isNil } from 'lodash-es'
|
|
4
|
-
import createControllableValue from './hooks/createControllableValue'
|
|
3
|
+
import { clamp, isNil } from 'lodash-es'
|
|
5
4
|
import { dispatchEventHandlerUnion } from './utils/solid'
|
|
6
5
|
|
|
7
6
|
export interface InputNumberProps
|
|
8
|
-
extends Omit<
|
|
7
|
+
extends Omit<
|
|
8
|
+
InputProps,
|
|
9
|
+
'defaultValue' | 'value' | 'onChange' | 'inputAfter' | 'onKeyDown' | 'min' | 'max' | 'suffix'
|
|
10
|
+
> {
|
|
9
11
|
defaultValue?: number | null | undefined
|
|
10
12
|
value?: number | null | undefined
|
|
11
13
|
onChange?: (value: number | null) => void
|
|
14
|
+
min?: number
|
|
15
|
+
max?: number
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
const isEmptyValue = (value: number | string | null | undefined) => isNil(value) || value === ''
|
|
15
19
|
|
|
16
|
-
const formatNum = (
|
|
17
|
-
v: number | string | null | undefined,
|
|
18
|
-
prev?: number | null | undefined,
|
|
19
|
-
): number | null => {
|
|
20
|
-
if (isEmptyValue(v)) {
|
|
21
|
-
return null
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const num = Number(v)
|
|
25
|
-
if (prev !== undefined && Number.isNaN(num)) {
|
|
26
|
-
return prev
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return num
|
|
30
|
-
}
|
|
31
|
-
|
|
32
20
|
const actionBtnClass =
|
|
33
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'
|
|
34
22
|
|
|
35
|
-
const InputNumber: Component<InputNumberProps> =
|
|
36
|
-
const
|
|
23
|
+
const InputNumber: Component<InputNumberProps> = _props => {
|
|
24
|
+
const props = mergeProps({
|
|
25
|
+
min: -Infinity,
|
|
26
|
+
max: Infinity,
|
|
27
|
+
}, _props)
|
|
28
|
+
const [_, inputProps] = splitProps(props, [
|
|
37
29
|
'defaultValue',
|
|
38
30
|
'value',
|
|
39
31
|
'onChange',
|
|
40
32
|
'onBlur',
|
|
41
33
|
])
|
|
42
34
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
setValue(v => {
|
|
49
|
-
if (isEmptyValue(v)) {
|
|
50
|
-
return addon
|
|
51
|
-
}
|
|
35
|
+
const clampValue = (v: number) => untrack(() => clamp(v, props.min, props.max))
|
|
36
|
+
|
|
37
|
+
let prev: number | null = null
|
|
38
|
+
const updatePrev = (v: number | null) => {
|
|
39
|
+
if (prev === v) return
|
|
52
40
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return v
|
|
56
|
-
}
|
|
57
|
-
return num + addon
|
|
58
|
-
})
|
|
41
|
+
prev = v
|
|
42
|
+
props.onChange?.(prev)
|
|
59
43
|
}
|
|
60
|
-
const up = () => { add(1); }
|
|
61
|
-
const down = () => { add(-1); }
|
|
62
44
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
+
|
|
52
|
+
const add = (addon: number) => {
|
|
53
|
+
let newValue: number | null
|
|
54
|
+
if (isEmptyValue(value())) {
|
|
55
|
+
newValue = clampValue(addon)
|
|
56
|
+
} else {
|
|
57
|
+
const num = Number(value())
|
|
58
|
+
newValue = Number.isNaN(num) ? null : clampValue(num + addon)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!Object.keys(props).includes('value')) {
|
|
62
|
+
setValue(newValue)
|
|
63
|
+
}
|
|
64
|
+
updatePrev(newValue)
|
|
65
|
+
}
|
|
66
|
+
const up = () => {
|
|
67
|
+
add(1)
|
|
68
|
+
}
|
|
69
|
+
const down = () => {
|
|
70
|
+
add(-1)
|
|
71
|
+
}
|
|
79
72
|
|
|
80
73
|
return (
|
|
81
74
|
<CommonInput
|
|
82
75
|
{...inputProps}
|
|
83
|
-
|
|
84
|
-
<div class="ant-flex ant-flex-col ant-h-full ant-w-24px ant-[border-left:
|
|
76
|
+
actions={
|
|
77
|
+
<div class="ant-flex ant-flex-col ant-h-full ant-w-24px ant-[border-left:1px_solid_var(--ant-color-border)]">
|
|
85
78
|
<div class={actionBtnClass} onClick={up}>
|
|
86
79
|
<div class="i-ant-design:up-outlined" />
|
|
87
80
|
</div>
|
|
88
|
-
<div
|
|
81
|
+
<div
|
|
82
|
+
class={`ant-[border-top:1px_solid_var(--ant-color-border)] ${actionBtnClass}`}
|
|
83
|
+
onClick={down}
|
|
84
|
+
>
|
|
89
85
|
<div class="i-ant-design:down-outlined" />
|
|
90
86
|
</div>
|
|
91
87
|
</div>
|
|
@@ -103,14 +99,24 @@ const InputNumber: Component<InputNumberProps> = props => {
|
|
|
103
99
|
}
|
|
104
100
|
}}
|
|
105
101
|
onChange={e => {
|
|
106
|
-
const newValue = e.target.value
|
|
102
|
+
const newValue = e.target.value
|
|
107
103
|
setValue(newValue)
|
|
104
|
+
|
|
105
|
+
let newValueNum: number | null = Number(newValue)
|
|
106
|
+
if (Number.isNaN(newValueNum)) return
|
|
107
|
+
|
|
108
|
+
if (isEmptyValue(newValue)) {
|
|
109
|
+
newValueNum = null
|
|
110
|
+
} else {
|
|
111
|
+
newValueNum = clampValue(newValueNum)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
updatePrev(newValueNum)
|
|
108
115
|
}}
|
|
109
116
|
onBlur={e => {
|
|
110
|
-
|
|
111
|
-
setValue(formatNum(newValue))
|
|
117
|
+
setValue(props.value ?? prev)
|
|
112
118
|
|
|
113
|
-
dispatchEventHandlerUnion(onBlur, e)
|
|
119
|
+
dispatchEventHandlerUnion(props.onBlur, e)
|
|
114
120
|
}}
|
|
115
121
|
/>
|
|
116
122
|
)
|
package/src/Modal.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type JSXElement, Show, createSignal, untrack, Ref } from 'solid-js'
|
|
1
|
+
import { type JSXElement, Show, createSignal, untrack, type Ref, mergeProps } from 'solid-js'
|
|
2
2
|
import { Portal, render } from 'solid-js/web'
|
|
3
3
|
import Button from './Button'
|
|
4
4
|
import cs from 'classnames'
|
|
@@ -11,10 +11,9 @@ export interface ModalInstance {
|
|
|
11
11
|
export interface ModalProps {
|
|
12
12
|
ref?: Ref<ModalInstance>
|
|
13
13
|
title?: JSXElement
|
|
14
|
-
|
|
14
|
+
defaultOpen?: boolean
|
|
15
15
|
width?: string
|
|
16
16
|
height?: string
|
|
17
|
-
// open?: boolean
|
|
18
17
|
children?: JSXElement
|
|
19
18
|
/**
|
|
20
19
|
* 垂直居中展示 Modal
|
|
@@ -29,29 +28,43 @@ export interface ModalProps {
|
|
|
29
28
|
* 设置为 false 时隐藏关闭按钮
|
|
30
29
|
*/
|
|
31
30
|
closeIcon?: boolean
|
|
31
|
+
footer?: boolean | ((modal: ModalInstance) => JSXElement)
|
|
32
|
+
/**
|
|
33
|
+
* 关闭时销毁 Modal 里的子元素
|
|
34
|
+
*/
|
|
35
|
+
destroyOnClose?: boolean
|
|
32
36
|
/**
|
|
33
37
|
* 返回 true,会自动关闭 modal
|
|
34
38
|
*/
|
|
35
|
-
onOk?: () =>
|
|
39
|
+
onOk?: () => boolean | Promise<boolean>
|
|
36
40
|
afterClose?: () => void
|
|
41
|
+
/**
|
|
42
|
+
* 自定义渲染对话框
|
|
43
|
+
*/
|
|
44
|
+
modalRender?: () => JSXElement
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
export interface MethodProps
|
|
40
48
|
extends Pick<ModalProps, 'title' | 'children' | 'onOk' | 'afterClose'> {}
|
|
41
49
|
|
|
42
|
-
function Modal(
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
props.afterClose?.()
|
|
47
|
-
}
|
|
50
|
+
function Modal(_props: ModalProps) {
|
|
51
|
+
const props = mergeProps({ footer: true }, _props)
|
|
52
|
+
const [open, setOpen] = createSignal(props.defaultOpen ?? false)
|
|
53
|
+
const [hide, setHide] = createSignal(false)
|
|
48
54
|
|
|
49
55
|
const instance: ModalInstance = {
|
|
50
56
|
open() {
|
|
51
57
|
setOpen(true)
|
|
58
|
+
setHide(false)
|
|
52
59
|
},
|
|
53
60
|
close() {
|
|
54
|
-
|
|
61
|
+
untrack(() => {
|
|
62
|
+
if (props.destroyOnClose) {
|
|
63
|
+
setOpen(false)
|
|
64
|
+
} else {
|
|
65
|
+
setHide(true)
|
|
66
|
+
}
|
|
67
|
+
})
|
|
55
68
|
},
|
|
56
69
|
}
|
|
57
70
|
untrack(() => {
|
|
@@ -60,6 +73,11 @@ function Modal(props: ModalProps) {
|
|
|
60
73
|
}
|
|
61
74
|
})
|
|
62
75
|
|
|
76
|
+
const close = () => {
|
|
77
|
+
instance.close()
|
|
78
|
+
props.afterClose?.()
|
|
79
|
+
}
|
|
80
|
+
|
|
63
81
|
const [confirmLoading, setConfirmLoading] = createSignal(false)
|
|
64
82
|
|
|
65
83
|
return (
|
|
@@ -75,61 +93,71 @@ function Modal(props: ModalProps) {
|
|
|
75
93
|
close()
|
|
76
94
|
}
|
|
77
95
|
}}
|
|
96
|
+
style={{ display: hide() ? 'none' : undefined }}
|
|
78
97
|
>
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
e
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
<Show when={typeof props.modalRender !== 'function'} fallback={props.modalRender!()}>
|
|
99
|
+
<div
|
|
100
|
+
class={cs(
|
|
101
|
+
'ant-absolute ant-px-24px ant-py-20px ant-rounded-8px ant-overflow-hidden ant-bg-white ant-flex ant-flex-col',
|
|
102
|
+
// '!ant-[animation-duration:.5s]',
|
|
103
|
+
!props.centered && 'ant-top-100px',
|
|
104
|
+
)}
|
|
105
|
+
onClick={e => {
|
|
106
|
+
e.stopPropagation()
|
|
107
|
+
}}
|
|
108
|
+
style={{
|
|
109
|
+
width: props.width ?? '520px',
|
|
110
|
+
height: props.height,
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
{/* 关闭按钮 */}
|
|
114
|
+
<Show when={props.closeIcon !== false}>
|
|
115
|
+
<Button
|
|
116
|
+
type="text"
|
|
117
|
+
class={cs(
|
|
118
|
+
'ant-rm-size-btn !ant-w-22px !ant-h-22px !ant-flex !ant-justify-center !ant-items-center ant-text-center ant-text-18px !ant-absolute !ant-top-16px !ant-right-16px ant-z-1000 ant-text-[rgba(0,0,0,.45)] hover:!ant-text-[rgba(0,0,0,.88)]',
|
|
119
|
+
)}
|
|
120
|
+
onClick={close}
|
|
121
|
+
>
|
|
122
|
+
<span class="i-ant-design:close-outlined" />
|
|
123
|
+
</Button>
|
|
124
|
+
</Show>
|
|
105
125
|
|
|
106
|
-
|
|
107
|
-
|
|
126
|
+
<div class="ant-text-[rgba(0,0,0,.88)] ant-text-16px ant-font-600 ant-mb-8px">
|
|
127
|
+
{props.title}
|
|
128
|
+
</div>
|
|
129
|
+
<div class="ant-grow">{props.children}</div>
|
|
108
130
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
131
|
+
<Show when={props.footer !== false}>
|
|
132
|
+
<div class="ant-mt-12px">
|
|
133
|
+
<Show when={typeof props.footer !== 'function'} fallback={typeof props.footer === 'function' && props.footer(instance)}>
|
|
134
|
+
<div class='ant-flex ant-gap-8px ant-justify-end'>
|
|
135
|
+
<Button onClick={close}>取消</Button>
|
|
136
|
+
<Button
|
|
137
|
+
type="primary"
|
|
138
|
+
loading={confirmLoading()}
|
|
139
|
+
// eslint-disable-next-line solid/reactivity, @typescript-eslint/no-misused-promises
|
|
140
|
+
onClick={async () => {
|
|
141
|
+
if (!props.onOk) return
|
|
118
142
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
143
|
+
let res = props.onOk?.()
|
|
144
|
+
if (res instanceof Promise) {
|
|
145
|
+
setConfirmLoading(true)
|
|
146
|
+
res = await res.finally(() => setConfirmLoading(false))
|
|
147
|
+
}
|
|
148
|
+
if (res) {
|
|
149
|
+
instance.close()
|
|
150
|
+
}
|
|
151
|
+
}}
|
|
152
|
+
>
|
|
153
|
+
确定
|
|
154
|
+
</Button>
|
|
155
|
+
</div>
|
|
156
|
+
</Show>
|
|
157
|
+
</div>
|
|
158
|
+
</Show>
|
|
131
159
|
</div>
|
|
132
|
-
</
|
|
160
|
+
</Show>
|
|
133
161
|
</div>
|
|
134
162
|
</Portal>
|
|
135
163
|
</Show>
|
|
@@ -153,7 +181,7 @@ Modal.warning = (props: MethodProps) => {
|
|
|
153
181
|
</>
|
|
154
182
|
}
|
|
155
183
|
children={<div class="ant-ml-34px">{props.children}</div>}
|
|
156
|
-
|
|
184
|
+
defaultOpen
|
|
157
185
|
afterClose={() => {
|
|
158
186
|
document.body.removeChild(div)
|
|
159
187
|
dispose()
|
package/src/Popconfirm.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Component, JSXElement, createSignal, mergeProps, untrack } from 'solid-js'
|
|
1
|
+
import { type Component, type JSXElement, createSignal, mergeProps, untrack, splitProps } from 'solid-js'
|
|
2
2
|
import Button from './Button'
|
|
3
|
-
import Tooltip from './Tooltip'
|
|
3
|
+
import Tooltip, { type TooltipProps } from './Tooltip'
|
|
4
4
|
|
|
5
|
-
interface PopconfirmProps {
|
|
5
|
+
interface PopconfirmProps extends Pick<TooltipProps, 'placement' | 'arrow'> {
|
|
6
6
|
title?: JSXElement
|
|
7
7
|
content?: JSXElement
|
|
8
8
|
children: JSXElement
|
|
@@ -22,6 +22,7 @@ interface PopconfirmProps {
|
|
|
22
22
|
|
|
23
23
|
const Popconfirm: Component<PopconfirmProps> = props => {
|
|
24
24
|
const mergedProps = mergeProps({ okText: '确定', cancelText: '取消' }, props)
|
|
25
|
+
const [tooltipProps] = splitProps(props, ['placement', 'arrow'])
|
|
25
26
|
const [open, setOpen] = createSignal(false)
|
|
26
27
|
|
|
27
28
|
return (
|
|
@@ -64,6 +65,7 @@ const Popconfirm: Component<PopconfirmProps> = props => {
|
|
|
64
65
|
</div>
|
|
65
66
|
</div>
|
|
66
67
|
}
|
|
68
|
+
{...tooltipProps}
|
|
67
69
|
>
|
|
68
70
|
{mergedProps.children}
|
|
69
71
|
</Tooltip>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { type Component, For, createSelector, type JSX, Show } from 'solid-js'
|
|
2
|
+
import cs from 'classnames'
|
|
3
|
+
import { type StringOrJSXElement, type Key } from '../types'
|
|
4
|
+
import createControllableValue from '../hooks/createControllableValue'
|
|
5
|
+
import { unwrapStringOrJSXElement } from '../utils/solid'
|
|
6
|
+
|
|
7
|
+
export interface SegmentedProps {
|
|
8
|
+
block?: boolean
|
|
9
|
+
disabled?: boolean
|
|
10
|
+
options: Array<
|
|
11
|
+
| string
|
|
12
|
+
| number
|
|
13
|
+
| {
|
|
14
|
+
label: StringOrJSXElement
|
|
15
|
+
value: string
|
|
16
|
+
disabled?: boolean
|
|
17
|
+
onClick?: (
|
|
18
|
+
e: MouseEvent & {
|
|
19
|
+
currentTarget: HTMLDivElement
|
|
20
|
+
target: Element
|
|
21
|
+
},
|
|
22
|
+
) => void
|
|
23
|
+
}
|
|
24
|
+
>
|
|
25
|
+
value?: Key
|
|
26
|
+
onChange?: (value: Key) => void
|
|
27
|
+
class?: string
|
|
28
|
+
style?: JSX.CSSProperties
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const unWarpValue = (value: SegmentedProps['options'][0]) =>
|
|
32
|
+
typeof value === 'object' ? value.value : value
|
|
33
|
+
|
|
34
|
+
const Segmented: Component<SegmentedProps> = props => {
|
|
35
|
+
const [value, setValue] = createControllableValue<Key>(props, {
|
|
36
|
+
defaultValue: unWarpValue(props.options[0]),
|
|
37
|
+
})
|
|
38
|
+
const isSelected = createSelector(value)
|
|
39
|
+
|
|
40
|
+
const isDisabledValue = (v: SegmentedProps['options'][0]) => {
|
|
41
|
+
if (props.disabled) return true
|
|
42
|
+
return typeof v === 'object' ? v.disabled : false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div
|
|
47
|
+
class={cs(
|
|
48
|
+
'ant-bg-[var(--ant-color-bg-layout)] ant-rounded-[var(--ant-border-radius)] ant-p-2px',
|
|
49
|
+
props.block ? 'ant-flex' : 'ant-inline-flex',
|
|
50
|
+
props.class,
|
|
51
|
+
)}
|
|
52
|
+
style={{
|
|
53
|
+
'--ant-segmented-item-color': 'rgba(0, 0, 0, 0.65)',
|
|
54
|
+
'--ant-segmented-item-hover-bg': 'rgba(0, 0, 0, 0.06)',
|
|
55
|
+
'--ant-segmented-item-active-bg': 'rgba(0, 0, 0, 0.15)',
|
|
56
|
+
...props.style,
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
<For each={props.options}>
|
|
60
|
+
{item => (
|
|
61
|
+
<div
|
|
62
|
+
class={cs(
|
|
63
|
+
props.block && 'ant-basis-0 ant-grow-1',
|
|
64
|
+
isDisabledValue(item) && 'ant-cursor-not-allowed',
|
|
65
|
+
)}
|
|
66
|
+
>
|
|
67
|
+
<div
|
|
68
|
+
class={cs(
|
|
69
|
+
'ant-rounded-[var(--ant-border-radius-sm)] ant-px-[var(--ant-padding-sm)] where:ant-cursor-pointer ant-leading-28px where:hover:ant-bg-[var(--ant-segmented-item-hover-bg)] where:active:ant-bg-[var(--ant-segmented-item-active-bg)]',
|
|
70
|
+
isSelected(unWarpValue(item)) &&
|
|
71
|
+
'ant-bg-white ant-shadow-[var(--ant-box-shadow-tertiary)]',
|
|
72
|
+
props.block && 'ant-flex ant-justify-center',
|
|
73
|
+
isDisabledValue(item) &&
|
|
74
|
+
'ant-[pointer-events:none] ant-text-[var(--ant-color-text-disabled)]',
|
|
75
|
+
)}
|
|
76
|
+
onClick={e => {
|
|
77
|
+
setValue(unWarpValue(item))
|
|
78
|
+
typeof item === 'object' && item.onClick?.(e)
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
<Show
|
|
82
|
+
when={typeof item !== 'object'}
|
|
83
|
+
fallback={typeof item === 'object' && unwrapStringOrJSXElement(item.label)}
|
|
84
|
+
>
|
|
85
|
+
{item as string | number}
|
|
86
|
+
</Show>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
)}
|
|
90
|
+
</For>
|
|
91
|
+
</div>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export default Segmented
|
package/src/Select.tsx
CHANGED
|
@@ -1,6 +1,128 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
type JSXElement,
|
|
3
|
+
type Component,
|
|
4
|
+
For,
|
|
5
|
+
createSelector,
|
|
6
|
+
createSignal,
|
|
7
|
+
Show,
|
|
8
|
+
createMemo,
|
|
9
|
+
} from 'solid-js'
|
|
10
|
+
import Tooltip from './Tooltip'
|
|
11
|
+
import { type Key } from './types'
|
|
12
|
+
import createControllableValue from './hooks/createControllableValue'
|
|
13
|
+
import cs from 'classnames'
|
|
14
|
+
import { useClickAway } from './hooks'
|
|
15
|
+
import { isNil } from 'lodash-es'
|
|
3
16
|
|
|
4
|
-
|
|
17
|
+
interface SelectOption {
|
|
18
|
+
label: JSXElement
|
|
19
|
+
value: Key
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface SelectProps {
|
|
23
|
+
value?: Key
|
|
24
|
+
onChange?: (value: Key) => void
|
|
25
|
+
options: SelectOption[]
|
|
26
|
+
placeholder?: string
|
|
27
|
+
allowClear?: boolean
|
|
28
|
+
class?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const Select: Component<SelectProps> = props => {
|
|
32
|
+
let select: HTMLDivElement
|
|
33
|
+
|
|
34
|
+
const [value, setValue] = createControllableValue<Key | undefined>(props)
|
|
35
|
+
const selectedValue = createSelector(value)
|
|
36
|
+
const selectedOption = createMemo(() => !isNil(value()) ? props.options.find(option => option.value === value()) : undefined)
|
|
37
|
+
|
|
38
|
+
const [open, setOpen] = createSignal(false)
|
|
39
|
+
useClickAway(
|
|
40
|
+
() => setOpen(false),
|
|
41
|
+
() => select!,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
const [width, setWidth] = createSignal(0)
|
|
45
|
+
const [hover, setHover] = createSignal(false)
|
|
46
|
+
const showClearBtn = createMemo(() => props.allowClear && hover() && !isNil(value()))
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Tooltip
|
|
50
|
+
mode="light"
|
|
51
|
+
open={open()}
|
|
52
|
+
onOpenChange={setOpen}
|
|
53
|
+
trigger={[]}
|
|
54
|
+
placement="bottomLeft"
|
|
55
|
+
arrow={false}
|
|
56
|
+
contentStyle={{
|
|
57
|
+
padding: 0,
|
|
58
|
+
}}
|
|
59
|
+
content={close => (
|
|
60
|
+
<div class="ant-bg-white ant-w-200px" style={{ width: `${width()}px` }}>
|
|
61
|
+
<For each={props.options}>
|
|
62
|
+
{item => (
|
|
63
|
+
<div
|
|
64
|
+
class={cs(
|
|
65
|
+
'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)]' : '',
|
|
67
|
+
)}
|
|
68
|
+
onClick={() => {
|
|
69
|
+
setValue(item.value)
|
|
70
|
+
close()
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
{item.label}
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
</For>
|
|
77
|
+
</div>
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
<div
|
|
81
|
+
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)}
|
|
83
|
+
tabIndex="0"
|
|
84
|
+
onClick={e => {
|
|
85
|
+
setOpen(true)
|
|
86
|
+
setWidth(e.currentTarget.offsetWidth)
|
|
87
|
+
e.currentTarget.focus()
|
|
88
|
+
}}
|
|
89
|
+
onMouseEnter={() => setHover(true)}
|
|
90
|
+
onMouseLeave={() => setHover(false)}
|
|
91
|
+
style={{
|
|
92
|
+
'--ant-arrow-color': 'rgba(146, 146, 146, 1)',
|
|
93
|
+
'--ant-clear-color': 'rgba(146, 146, 146, 1)',
|
|
94
|
+
'--ant-clear-color-hover': 'rgba(194, 194, 194, 1)',
|
|
95
|
+
}}
|
|
96
|
+
>
|
|
97
|
+
<div class="ant-relative ant-h-full">
|
|
98
|
+
<Show
|
|
99
|
+
when={!isNil(value())}
|
|
100
|
+
fallback={
|
|
101
|
+
<input
|
|
102
|
+
class="ant-h-full ant-w-full ant-float-left ant-[outline:none]"
|
|
103
|
+
readOnly
|
|
104
|
+
placeholder={props.placeholder}
|
|
105
|
+
/>
|
|
106
|
+
}
|
|
107
|
+
>
|
|
108
|
+
<div>{selectedOption()!.label ?? value()}</div>
|
|
109
|
+
</Show>
|
|
110
|
+
|
|
111
|
+
<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)]" />}>
|
|
113
|
+
<span
|
|
114
|
+
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
|
+
onClick={e => {
|
|
116
|
+
e.stopPropagation()
|
|
117
|
+
setValue(undefined)
|
|
118
|
+
}}
|
|
119
|
+
/>
|
|
120
|
+
</Show>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</Tooltip>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
5
127
|
|
|
6
128
|
export default Select
|
package/src/Switch.tsx
CHANGED
|
@@ -9,7 +9,7 @@ export interface SwitchProps {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const Switch: Component<SwitchProps> = props => {
|
|
12
|
-
const [checked, setChecked] = createControllableValue(props, {
|
|
12
|
+
const [checked, setChecked] = createControllableValue<boolean>(props, {
|
|
13
13
|
defaultValuePropName: 'defaultChecked',
|
|
14
14
|
valuePropName: 'checked',
|
|
15
15
|
})
|