antd-solid 0.0.2

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 (54) hide show
  1. package/.eslintrc.cjs +36 -0
  2. package/.prettierrc +11 -0
  3. package/.vscode/settings.json +13 -0
  4. package/README.md +11 -0
  5. package/docs/.vitepress/components/Code.vue +59 -0
  6. package/docs/.vitepress/config.ts +49 -0
  7. package/docs/.vitepress/theme/index.css +15 -0
  8. package/docs/.vitepress/theme/index.ts +13 -0
  9. package/docs/components/Button.tsx +20 -0
  10. package/docs/components/Table.tsx +34 -0
  11. package/docs/components/button.md +23 -0
  12. package/docs/components/table.md +23 -0
  13. package/docs/index.md +28 -0
  14. package/package.json +62 -0
  15. package/rollup.config.js +25 -0
  16. package/src/Button.css +14 -0
  17. package/src/Button.tsx +86 -0
  18. package/src/ColorPicker.tsx +66 -0
  19. package/src/DatePicker.tsx +12 -0
  20. package/src/Form.tsx +98 -0
  21. package/src/Image.tsx +29 -0
  22. package/src/Input.tsx +110 -0
  23. package/src/InputNumber.test.tsx +46 -0
  24. package/src/InputNumber.tsx +119 -0
  25. package/src/Modal.tsx +168 -0
  26. package/src/Popconfirm.tsx +73 -0
  27. package/src/Popover.tsx +30 -0
  28. package/src/Progress.tsx +4 -0
  29. package/src/Radio.tsx +132 -0
  30. package/src/Result.tsx +38 -0
  31. package/src/Select.tsx +6 -0
  32. package/src/Skeleton.tsx +14 -0
  33. package/src/Spin.tsx +23 -0
  34. package/src/Switch.tsx +34 -0
  35. package/src/Table.tsx +46 -0
  36. package/src/Tabs.tsx +88 -0
  37. package/src/Timeline.tsx +33 -0
  38. package/src/Tooltip.tsx +209 -0
  39. package/src/Tree.tsx +246 -0
  40. package/src/Upload.tsx +10 -0
  41. package/src/hooks/createControllableValue.ts +65 -0
  42. package/src/hooks/createUpdateEffect.ts +16 -0
  43. package/src/hooks/index.ts +2 -0
  44. package/src/hooks/useClickAway.ts +18 -0
  45. package/src/hooks/useSize.ts +26 -0
  46. package/src/index.css +21 -0
  47. package/src/index.ts +37 -0
  48. package/src/utils/ReactToSolid.tsx +38 -0
  49. package/src/utils/SolidToReact.tsx +27 -0
  50. package/src/utils/array.ts +21 -0
  51. package/src/utils/component.tsx +85 -0
  52. package/src/utils/solid.ts +48 -0
  53. package/tsconfig.json +23 -0
  54. package/unocss.config.ts +92 -0
package/src/Form.tsx ADDED
@@ -0,0 +1,98 @@
1
+ import { set } from 'lodash-es'
2
+ import {
3
+ type JSXElement,
4
+ type Component,
5
+ type JSX,
6
+ mergeProps,
7
+ Show,
8
+ Index,
9
+ createMemo,
10
+ } from 'solid-js'
11
+ import { Dynamic } from 'solid-js/web'
12
+ import { toArray } from './utils/array'
13
+ import cs from 'classnames'
14
+
15
+ export interface FormInstance<T extends {} = {}> {
16
+ validateFields: () => Promise<T>
17
+ }
18
+
19
+ export interface FormProps<T extends {} = {}> {
20
+ ref?: (form: FormInstance<T>) => void;
21
+ /**
22
+ * 表单布局
23
+ * 默认: horizontal
24
+ */
25
+ layout?: 'horizontal' | 'vertical' | 'inline'
26
+ /**
27
+ * 提交按钮
28
+ * @deprecated
29
+ */
30
+ submit?: (form: FormInstance<T>) => JSXElement
31
+ children: JSXElement
32
+ }
33
+
34
+ export interface FormItemComponentProps<T = any> {
35
+ defaultValue?: T
36
+ onChange?: (value: T) => void
37
+ }
38
+
39
+ export interface FormItemProps {
40
+ class?: string
41
+ style?: JSX.CSSProperties
42
+ required?: boolean
43
+ label?: JSXElement
44
+ name?: string
45
+ initialValue?: any
46
+ component: Component<FormItemComponentProps>
47
+ }
48
+
49
+ function Form<T extends {} = {}>(_props: FormProps<T>) {
50
+ const props = mergeProps({ layout: 'horizontal' } as FormProps, _props)
51
+
52
+ const resolvedChildren = createMemo(() => {
53
+ return toArray(props.children) as unknown as FormItemProps[]
54
+ })
55
+
56
+ const values = Object.fromEntries(
57
+ resolvedChildren().map(child => [child.name, child.initialValue]),
58
+ ) as T
59
+ const formInstance: FormInstance<T> = {
60
+ async validateFields() {
61
+ return await Promise.resolve(values)
62
+ },
63
+ }
64
+ _props.ref?.(formInstance)
65
+
66
+ return (
67
+ <div>
68
+ <Index each={resolvedChildren()}>
69
+ {item => (
70
+ <div class={cs('ant-flex ant-items-center ant-mb-16px', item().class)} style={item().style}>
71
+ <span class="ant-flex-shrink-0 ant-mr-8px">
72
+ <Show when={item().required}>
73
+ <span class='ant-mr-4px ant-text-[var(--error-color)]'>*</span>
74
+ </Show>
75
+ <label>{item().label}</label>
76
+ </span>
77
+
78
+ <Dynamic
79
+ component={item().component}
80
+ defaultValue={item().initialValue}
81
+ onChange={(value: any) => {
82
+ set(values, item().name!, value)
83
+ }}
84
+ />
85
+ </div>
86
+ )}
87
+ </Index>
88
+
89
+ {props.submit?.(formInstance as FormInstance<T>)}
90
+ </div>
91
+ )
92
+ }
93
+
94
+ Form.Item = (props: FormItemProps) => props as any
95
+
96
+ Form.createForm = () => {}
97
+
98
+ export default Form
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,110 @@
1
+ import { omit } from 'lodash-es'
2
+ import { Show, 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
+
8
+ type CommonInputProps<T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> = JSX.CustomAttributes<T> & {
9
+ textarea?: boolean
10
+ defaultValue?: string | undefined
11
+ value?: string | undefined
12
+ addonBefore?: JSXElement
13
+ addonAfter?: JSXElement
14
+ /**
15
+ * 仅供 InputNumber 使用
16
+ */
17
+ inputAfter?: JSXElement
18
+ placeholder?: string
19
+ onChange?: JSX.InputEventHandler<T, InputEvent>
20
+ onPressEnter?: JSX.EventHandler<T, KeyboardEvent>
21
+ onKeyDown?: JSX.EventHandler<T, KeyboardEvent>
22
+ }
23
+
24
+ export function CommonInput<T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement>(props: CommonInputProps<T> &
25
+ Omit<JSX.InputHTMLAttributes<T>, 'onChange' | 'onInput' | 'onKeyDown'>) {
26
+ const [{ onChange, onPressEnter, onKeyDown }, inputProps] = splitProps(props, [
27
+ 'defaultValue',
28
+ 'value',
29
+ 'class',
30
+ 'addonBefore',
31
+ 'addonAfter',
32
+ 'inputAfter',
33
+ 'onChange',
34
+ 'onPressEnter',
35
+ 'onKeyDown',
36
+ ])
37
+
38
+ const [_, controllableProps] = splitProps(props, ['onChange'])
39
+ const [value, setValue] = createControllableValue(controllableProps)
40
+
41
+ return (
42
+ <div class="ant-flex ant-w-full">
43
+ <Show when={props.addonBefore}>
44
+ <div class="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_#d9d9d9] ant-border-r-0 ant-rounded-l-6px ant-text-14px">
45
+ {props.addonBefore}
46
+ </div>
47
+ </Show>
48
+
49
+ <div class="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)]">
50
+ <Dynamic<Component<JSX.InputHTMLAttributes<HTMLInputElement>>>
51
+ component={
52
+ (props.textarea ? 'textarea' : 'input') as unknown as Component<
53
+ JSX.InputHTMLAttributes<HTMLInputElement>
54
+ >
55
+ }
56
+ {...inputProps as JSX.InputHTMLAttributes<HTMLInputElement>}
57
+ class={cs(
58
+ 'ant-w-full ant-py-0 ant-px-11px ant-[outline:none] ant-text-14px ant-rounded-6px ant-[border:1px_solid_#d9d9d9] focus:ant-border-[var(--primary-color)] focus:ant-[box-shadow:0_0_0_2px_rgba(5,145,255,0.1)] ant-py-8px',
59
+ !props.textarea && 'ant-h-32px',
60
+ props.class,
61
+ props.addonBefore && 'ant-rounded-l-0',
62
+ props.addonAfter && 'ant-rounded-r-0',
63
+ )}
64
+ value={value() ?? ''}
65
+ onInput={e => {
66
+ setValue(e.target.value)
67
+ onChange?.(e as any)
68
+ }}
69
+ onKeyDown={e => {
70
+ if (e.key === 'Enter') {
71
+ onPressEnter?.(e as any)
72
+ }
73
+
74
+ onKeyDown?.(e as any)
75
+ }}
76
+ />
77
+
78
+ <Show when={props.inputAfter}>
79
+ <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">
80
+ {props.inputAfter}
81
+ </div>
82
+ </Show>
83
+ </div>
84
+
85
+ <Show when={props.addonAfter}>
86
+ <div class="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_#d9d9d9] ant-border-l-0 ant-rounded-r-6px ant-text-14px">
87
+ {props.addonAfter}
88
+ </div>
89
+ </Show>
90
+ </div>
91
+ )
92
+ }
93
+
94
+ export type InputProps = Omit<CommonInputProps, 'inputAfter' | 'textarea'> &
95
+ Omit<JSX.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'onInput' | 'onKeyDown'>
96
+
97
+ export type TextAreaProps = Omit<CommonInputProps<HTMLTextAreaElement>, 'inputAfter' | 'textarea'> &
98
+ Omit<JSX.TextareaHTMLAttributes<HTMLTextAreaElement>, 'onChange' | 'onInput' | 'onKeyDown'>
99
+
100
+ const Input: Component<InputProps> & {
101
+ TextArea: Component<TextAreaProps>
102
+ } = props => {
103
+ return <CommonInput {...omit(props, ['inputAfter'])} />
104
+ }
105
+
106
+ Input.TextArea = props => {
107
+ return <CommonInput<HTMLTextAreaElement> textarea {...omit(props, ['inputAfter'])} />
108
+ }
109
+
110
+ 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
+ })
@@ -0,0 +1,119 @@
1
+ import { type Component, createEffect, on, splitProps } from 'solid-js'
2
+ import { CommonInput, type InputProps } from './Input'
3
+ import { isNil } from 'lodash-es'
4
+ import createControllableValue from './hooks/createControllableValue'
5
+ import { dispatchEventHandlerUnion } from './utils/solid'
6
+
7
+ export interface InputNumberProps
8
+ extends Omit<InputProps, 'defaultValue' | 'value' | 'onChange' | 'inputAfter' | 'onKeyDown'> {
9
+ defaultValue?: number | null | undefined
10
+ value?: number | null | undefined
11
+ onChange?: (value: number | null) => void
12
+ }
13
+
14
+ const isEmptyValue = (value: number | string | null | undefined) => isNil(value) || value === ''
15
+
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
+ const actionBtnClass =
33
+ '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
+
35
+ const InputNumber: Component<InputNumberProps> = props => {
36
+ const [{ onChange, onBlur }, inputProps] = splitProps(props, [
37
+ 'defaultValue',
38
+ 'value',
39
+ 'onChange',
40
+ 'onBlur',
41
+ ])
42
+
43
+ const [_, controllableProps] = splitProps(props, ['onChange'])
44
+ const [value, setValue] = createControllableValue<number | string | null | undefined>(
45
+ controllableProps,
46
+ )
47
+ const add = (addon: number) => {
48
+ setValue(v => {
49
+ if (isEmptyValue(v)) {
50
+ return addon
51
+ }
52
+
53
+ const num = Number(v)
54
+ if (Number.isNaN(num)) {
55
+ return v
56
+ }
57
+ return num + addon
58
+ })
59
+ }
60
+ const up = () => { add(1); }
61
+ const down = () => { add(-1); }
62
+
63
+ createEffect(
64
+ on(
65
+ value,
66
+ (input, __, prev: number | null | undefined) => {
67
+ const num = formatNum(input, prev)
68
+ if (num !== prev) {
69
+ prev = num
70
+ onChange?.(num)
71
+ }
72
+ return num
73
+ },
74
+ {
75
+ defer: true,
76
+ },
77
+ ),
78
+ )
79
+
80
+ return (
81
+ <CommonInput
82
+ {...inputProps}
83
+ inputAfter={
84
+ <div class="ant-flex ant-flex-col ant-h-full ant-w-24px ant-[border-left:1px_solid_#d9d9d9]">
85
+ <div class={actionBtnClass} onClick={up}>
86
+ <div class="i-ant-design:up-outlined" />
87
+ </div>
88
+ <div class={`ant-[border-top:1px_solid_#d9d9d9] ${actionBtnClass}`} onClick={down}>
89
+ <div class="i-ant-design:down-outlined" />
90
+ </div>
91
+ </div>
92
+ }
93
+ value={`${value() ?? ''}`}
94
+ onKeyDown={e => {
95
+ switch (e.key) {
96
+ case 'ArrowUp':
97
+ up()
98
+ e.preventDefault()
99
+ return
100
+ case 'ArrowDown':
101
+ down()
102
+ e.preventDefault()
103
+ }
104
+ }}
105
+ onChange={e => {
106
+ const newValue = e.target.value || null
107
+ setValue(newValue)
108
+ }}
109
+ onBlur={e => {
110
+ const newValue = e.target.value || null
111
+ setValue(formatNum(newValue))
112
+
113
+ dispatchEventHandlerUnion(onBlur, e)
114
+ }}
115
+ />
116
+ )
117
+ }
118
+
119
+ export default InputNumber
package/src/Modal.tsx ADDED
@@ -0,0 +1,168 @@
1
+ import { type JSXElement, Show, createSignal, untrack, Ref } from 'solid-js'
2
+ import { Portal, render } from 'solid-js/web'
3
+ import Button from './Button'
4
+ import cs from 'classnames'
5
+
6
+ export interface ModalInstance {
7
+ open: () => void
8
+ close: () => void
9
+ }
10
+
11
+ export interface ModalProps {
12
+ ref?: Ref<ModalInstance>
13
+ title?: JSXElement
14
+ initialOpen?: boolean
15
+ width?: string
16
+ height?: string
17
+ // open?: boolean
18
+ children?: JSXElement
19
+ /**
20
+ * 垂直居中展示 Modal
21
+ */
22
+ centered?: boolean
23
+ /**
24
+ * 点击蒙层是否允许关闭
25
+ * 默认 true
26
+ */
27
+ maskClosable?: boolean
28
+ /**
29
+ * 设置为 false 时隐藏关闭按钮
30
+ */
31
+ closeIcon?: boolean
32
+ /**
33
+ * 返回 true,会自动关闭 modal
34
+ */
35
+ onOk?: () => (boolean | Promise<boolean>)
36
+ afterClose?: () => void
37
+ }
38
+
39
+ export interface MethodProps
40
+ extends Pick<ModalProps, 'title' | 'children' | 'onOk' | 'afterClose'> {}
41
+
42
+ function Modal(props: ModalProps) {
43
+ const [open, setOpen] = createSignal(props.initialOpen ?? false)
44
+ const close = () => {
45
+ setOpen(false)
46
+ props.afterClose?.()
47
+ }
48
+
49
+ const instance: ModalInstance = {
50
+ open() {
51
+ setOpen(true)
52
+ },
53
+ close() {
54
+ setOpen(false)
55
+ },
56
+ }
57
+ untrack(() => {
58
+ if (typeof props.ref === 'function') {
59
+ props.ref?.(instance)
60
+ }
61
+ })
62
+
63
+ const [confirmLoading, setConfirmLoading] = createSignal(false)
64
+
65
+ return (
66
+ <Show when={open()}>
67
+ <Portal>
68
+ <div
69
+ class={cs(
70
+ 'ant-fixed ant-justify-center ant-inset-0 ant-bg-[rgba(0,0,0,.45)] ant-flex ant-z-1000',
71
+ props.centered && 'ant-items-center',
72
+ )}
73
+ onClick={() => {
74
+ if (props.maskClosable ?? true) {
75
+ close()
76
+ }
77
+ }}
78
+ >
79
+ <div
80
+ class={cs(
81
+ 'ant-absolute ant-px-24px ant-py-20px ant-rounded-8px ant-overflow-hidden ant-bg-white ant-flex ant-flex-col',
82
+ // '!ant-[animation-duration:.5s]',
83
+ !props.centered && 'ant-top-100px',
84
+ )}
85
+ onClick={e => {
86
+ e.stopPropagation()
87
+ }}
88
+ style={{
89
+ width: props.width ?? '520px',
90
+ height: props.height,
91
+ }}
92
+ >
93
+ {/* 关闭按钮 */}
94
+ <Show when={props.closeIcon !== false}>
95
+ <Button
96
+ type="text"
97
+ class={cs(
98
+ '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)]',
99
+ )}
100
+ onClick={close}
101
+ >
102
+ <span class="i-ant-design:close-outlined" />
103
+ </Button>
104
+ </Show>
105
+
106
+ <div class="ant-text-[rgba(0,0,0,.88)] ant-text-16px ant-font-600 ant-mb-8px">{props.title}</div>
107
+ <div class='ant-grow'>{props.children}</div>
108
+
109
+ <div class="ant-text-right ant-mt-12px">
110
+ <Button onClick={close}>取消</Button>
111
+ <Button
112
+ type="primary"
113
+ class="!ant-ml-8px"
114
+ loading={confirmLoading()}
115
+ // eslint-disable-next-line solid/reactivity, @typescript-eslint/no-misused-promises
116
+ onClick={async () => {
117
+ if (!props.onOk) return
118
+
119
+ let res = props.onOk?.()
120
+ if (res instanceof Promise) {
121
+ setConfirmLoading(true)
122
+ res = await res.finally(() => setConfirmLoading(false))
123
+ }
124
+ if (res) {
125
+ instance.close()
126
+ }
127
+ }}
128
+ >
129
+ 确定
130
+ </Button>
131
+ </div>
132
+ </div>
133
+ </div>
134
+ </Portal>
135
+ </Show>
136
+ )
137
+ }
138
+
139
+ Modal.warning = (props: MethodProps) => {
140
+ const div = document.createElement('div')
141
+ document.body.appendChild(div)
142
+ const dispose = render(
143
+ () => (
144
+ <Modal
145
+ width="416px"
146
+ maskClosable={false}
147
+ closeIcon={false}
148
+ {...props}
149
+ title={
150
+ <>
151
+ <span class="i-ant-design:exclamation-circle ant-text-22px ant-mr-12px ant-text-[var(--warning-color)]" />
152
+ {props.title}
153
+ </>
154
+ }
155
+ children={<div class="ant-ml-34px">{props.children}</div>}
156
+ initialOpen
157
+ afterClose={() => {
158
+ document.body.removeChild(div)
159
+ dispose()
160
+ props.afterClose?.()
161
+ }}
162
+ />
163
+ ),
164
+ div,
165
+ )
166
+ }
167
+
168
+ export default Modal
@@ -0,0 +1,73 @@
1
+ import { Component, JSXElement, createSignal, mergeProps, untrack } from 'solid-js'
2
+ import Button from './Button'
3
+ import Tooltip from './Tooltip'
4
+
5
+ interface PopconfirmProps {
6
+ title?: JSXElement
7
+ content?: JSXElement
8
+ children: JSXElement
9
+ onCancel?: () => void
10
+ onConfirm?: () => void
11
+ /**
12
+ * 确认按钮文字
13
+ * 默认:确定
14
+ */
15
+ okText?: string
16
+ /**
17
+ * 取消按钮文字
18
+ * 默认:取消
19
+ */
20
+ cancelText?: string
21
+ }
22
+
23
+ const Popconfirm: Component<PopconfirmProps> = props => {
24
+ const mergedProps = mergeProps({ okText: '确定', cancelText: '取消' }, props)
25
+ const [open, setOpen] = createSignal(false)
26
+
27
+ return (
28
+ <Tooltip
29
+ mode="light"
30
+ trigger="click"
31
+ open={open()}
32
+ onOpenChange={setOpen}
33
+ content={
34
+ <div>
35
+ <div class="ant-mb-8px ant-flex ant-items-center">
36
+ <span class="i-ant-design:exclamation-circle-fill ant-text-#faad14" />
37
+ <span class="ant-ml-8px ant-text-[rgba(0,0,0,0.88)] ant-font-600">{mergedProps.title}</span>
38
+ </div>
39
+
40
+ <div class="ant-ml-22px ant-mb-8px ant-text-[rgba(0,0,0,0.88)]">{mergedProps.content}</div>
41
+
42
+ <div class="ant-text-right">
43
+ <Button
44
+ class="ant-ml-8px"
45
+ size="small"
46
+ onClick={() => {
47
+ setOpen(false)
48
+ untrack(() => mergedProps.onCancel?.())
49
+ }}
50
+ >
51
+ {mergedProps.cancelText}
52
+ </Button>
53
+ <Button
54
+ class="ant-ml-8px"
55
+ type="primary"
56
+ size="small"
57
+ onClick={() => {
58
+ setOpen(false)
59
+ untrack(() => mergedProps.onConfirm?.())
60
+ }}
61
+ >
62
+ {mergedProps.okText}
63
+ </Button>
64
+ </div>
65
+ </div>
66
+ }
67
+ >
68
+ {mergedProps.children}
69
+ </Tooltip>
70
+ )
71
+ }
72
+
73
+ export default Popconfirm
@@ -0,0 +1,30 @@
1
+ import { type Component, type JSXElement, Show } from 'solid-js'
2
+ import Tooltip, { Content, type TooltipProps } from './Tooltip'
3
+
4
+ interface PopoverProps extends TooltipProps {
5
+ title?: JSXElement
6
+ }
7
+
8
+ const Popover: Component<PopoverProps> = props => {
9
+ return (
10
+ <Tooltip
11
+ mode="light"
12
+ {...props}
13
+ content={close =>
14
+ <div>
15
+ <Show when={props.title}>
16
+ <div class="ant-mb-8px ant-flex ant-items-center">
17
+ <span class="ant-text-[rgba(0,0,0,0.88)] ant-font-600">{props.title}</span>
18
+ </div>
19
+ </Show>
20
+
21
+ <div class="ant-text-[rgba(0,0,0,0.88)]">
22
+ <Content content={props.content} close={close} />
23
+ </div>
24
+ </div>
25
+ }
26
+ />
27
+ )
28
+ }
29
+
30
+ export default Popover
@@ -0,0 +1,4 @@
1
+ import { Progress as ProgressAntd } from 'antd'
2
+ import { reactToSolidComponent, replaceChildren, replaceClassName } from './utils/component'
3
+
4
+ export default replaceChildren(replaceClassName(reactToSolidComponent(ProgressAntd)))