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.
Files changed (105) hide show
  1. package/.eslintignore +2 -0
  2. package/.husky/pre-commit +4 -0
  3. package/.vscode/settings.json +1 -1
  4. package/docs/.vitepress/components/Code.vue +127 -11
  5. package/docs/.vitepress/config.ts +39 -1
  6. package/docs/.vitepress/theme/index.css +4 -0
  7. package/docs/.vitepress/theme/index.ts +0 -1
  8. package/docs/components/{Button.tsx → button/Base.tsx} +1 -0
  9. package/docs/components/button/Danger.tsx +21 -0
  10. package/docs/components/button.md +19 -7
  11. package/docs/components/collapse/Base.tsx +25 -0
  12. package/docs/components/collapse.md +26 -0
  13. package/docs/components/color-picker/Base.tsx +14 -0
  14. package/docs/components/color-picker.md +26 -0
  15. package/docs/components/compact/Base.tsx +27 -0
  16. package/docs/components/compact.md +26 -0
  17. package/docs/components/date-picker/Base.tsx +15 -0
  18. package/docs/components/date-picker.md +26 -0
  19. package/docs/components/empty/Base.tsx +8 -0
  20. package/docs/components/empty/PRESENTED_IMAGE_SIMPLE.tsx +8 -0
  21. package/docs/components/empty.md +32 -0
  22. package/docs/components/form/Base.tsx +63 -0
  23. package/docs/components/form.md +27 -0
  24. package/docs/components/image/Base.tsx +13 -0
  25. package/docs/components/image.md +26 -0
  26. package/docs/components/input/AddonBeforeAndAfter.tsx +14 -0
  27. package/docs/components/input/Base.tsx +15 -0
  28. package/docs/components/input/Disabled.tsx +19 -0
  29. package/docs/components/input/PrefixAndSuffix.tsx +14 -0
  30. package/docs/components/input/Status.tsx +25 -0
  31. package/docs/components/input/TextArea.tsx +14 -0
  32. package/docs/components/input-number/Base.tsx +15 -0
  33. package/docs/components/input-number/MinMax.tsx +17 -0
  34. package/docs/components/input-number.md +34 -0
  35. package/docs/components/input.md +57 -0
  36. package/docs/components/modal/Base.tsx +35 -0
  37. package/docs/components/modal.md +26 -0
  38. package/docs/components/popconfirm/Base.tsx +25 -0
  39. package/docs/components/popconfirm.md +26 -0
  40. package/docs/components/popover/Base.tsx +19 -0
  41. package/docs/components/popover.md +26 -0
  42. package/docs/components/progress/Base.tsx +16 -0
  43. package/docs/components/progress.md +26 -0
  44. package/docs/components/radio/Base.tsx +10 -0
  45. package/docs/components/radio.md +26 -0
  46. package/docs/components/segmented/Base.tsx +10 -0
  47. package/docs/components/segmented/Block.tsx +10 -0
  48. package/docs/components/segmented/Disabled.tsx +28 -0
  49. package/docs/components/segmented.md +38 -0
  50. package/docs/components/select/AllowClear.tsx +18 -0
  51. package/docs/components/select/Base.tsx +17 -0
  52. package/docs/components/select.md +35 -0
  53. package/docs/components/switch/Base.tsx +14 -0
  54. package/docs/components/switch.md +26 -0
  55. package/docs/components/{Table.tsx → table/Base.tsx} +2 -2
  56. package/docs/components/table.md +12 -6
  57. package/docs/components/tabs/Base.tsx +25 -0
  58. package/docs/components/tabs/Segment.tsx +25 -0
  59. package/docs/components/tabs.md +33 -0
  60. package/docs/components/tooltip/Base.tsx +12 -0
  61. package/docs/components/tooltip/Placement.tsx +90 -0
  62. package/docs/components/tooltip.md +32 -0
  63. package/docs/components/tree/Base.tsx +45 -0
  64. package/docs/components/tree.md +26 -0
  65. package/global.d.ts +9 -0
  66. package/package.json +29 -14
  67. package/patches/vitepress@1.0.0-rc.4.patch +60 -0
  68. package/rollup.config.js +60 -20
  69. package/scripts/annotationNonProductionCode.js +30 -0
  70. package/scripts/cancelAnnotationNonProductionCode.js +30 -0
  71. package/src/Button.tsx +51 -12
  72. package/src/Collapse/index.tsx +86 -0
  73. package/src/ColorPicker.tsx +6 -61
  74. package/src/Compact.tsx +15 -0
  75. package/src/Empty/PRESENTED_IMAGE_SIMPLE.tsx +15 -0
  76. package/src/Empty/assets/EmptySvg.tsx +43 -0
  77. package/src/Empty/assets/SimpleEmptySvg.tsx +16 -0
  78. package/src/Empty/index.tsx +20 -0
  79. package/src/Input.tsx +154 -62
  80. package/src/InputNumber.tsx +68 -62
  81. package/src/Modal.tsx +90 -62
  82. package/src/Popconfirm.tsx +5 -3
  83. package/src/Segmented/index.tsx +95 -0
  84. package/src/Select.tsx +125 -3
  85. package/src/Switch.tsx +1 -1
  86. package/src/Table.tsx +36 -29
  87. package/src/Tabs.tsx +88 -45
  88. package/src/Tooltip.tsx +233 -87
  89. package/src/Tree.tsx +4 -4
  90. package/src/form/Form.tsx +94 -0
  91. package/src/form/FormItem.tsx +139 -0
  92. package/src/form/context.ts +16 -0
  93. package/src/form/index.ts +13 -0
  94. package/src/hooks/createControllableValue.ts +9 -6
  95. package/src/index.ts +12 -5
  96. package/src/types/index.ts +5 -0
  97. package/src/utils/EventEmitter.ts +15 -0
  98. package/src/utils/component.tsx +1 -1
  99. package/src/utils/solid.ts +8 -3
  100. package/src/utils/zh_CN.ts +236 -0
  101. package/tsconfig.json +6 -4
  102. package/unocss.config.ts +90 -6
  103. package/src/Button.css +0 -14
  104. package/src/Form.tsx +0 -98
  105. package/src/index.css +0 -21
@@ -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<InputProps, 'defaultValue' | 'value' | 'onChange' | 'inputAfter' | 'onKeyDown'> {
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> = props => {
36
- const [{ onChange, onBlur }, inputProps] = splitProps(props, [
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 [_, 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
- }
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
- const num = Number(v)
54
- if (Number.isNaN(num)) {
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
- 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
- )
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
- inputAfter={
84
- <div class="ant-flex ant-flex-col ant-h-full ant-w-24px ant-[border-left:1px_solid_#d9d9d9]">
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 class={`ant-[border-top:1px_solid_#d9d9d9] ${actionBtnClass}`} onClick={down}>
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 || null
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
- const newValue = e.target.value || null
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
- initialOpen?: boolean
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?: () => (boolean | Promise<boolean>)
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(props: ModalProps) {
43
- const [open, setOpen] = createSignal(props.initialOpen ?? false)
44
- const close = () => {
45
- setOpen(false)
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
- setOpen(false)
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
- <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>
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
- <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>
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
- <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
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
- 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>
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
- </div>
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
- initialOpen
184
+ defaultOpen
157
185
  afterClose={() => {
158
186
  document.body.removeChild(div)
159
187
  dispose()
@@ -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 { Select as SelectAntd } from 'antd'
2
- import { reactToSolidComponent, replaceClassName } from './utils/component'
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
- const Select = replaceClassName(reactToSolidComponent(SelectAntd))
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
  })