antd-solid 0.0.5 → 0.0.7

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 (48) hide show
  1. package/package.json +4 -3
  2. package/src/Button.tsx +125 -0
  3. package/src/Collapse/index.tsx +86 -0
  4. package/src/ColorPicker.tsx +11 -0
  5. package/src/Compact.tsx +15 -0
  6. package/src/DatePicker.tsx +22 -0
  7. package/src/Empty/PRESENTED_IMAGE_SIMPLE.tsx +15 -0
  8. package/src/Empty/assets/EmptySvg.tsx +43 -0
  9. package/src/Empty/assets/SimpleEmptySvg.tsx +16 -0
  10. package/src/Empty/index.tsx +20 -0
  11. package/src/Image.tsx +29 -0
  12. package/src/Input.tsx +202 -0
  13. package/src/InputNumber.test.tsx +46 -0
  14. package/src/InputNumber.tsx +125 -0
  15. package/src/Modal.tsx +196 -0
  16. package/src/Popconfirm.tsx +75 -0
  17. package/src/Popover.tsx +30 -0
  18. package/src/Progress.tsx +4 -0
  19. package/src/Radio.tsx +132 -0
  20. package/src/Result.tsx +38 -0
  21. package/src/Segmented/index.tsx +95 -0
  22. package/src/Select.tsx +128 -0
  23. package/src/Skeleton.tsx +14 -0
  24. package/src/Spin.tsx +23 -0
  25. package/src/Switch.tsx +34 -0
  26. package/src/Table.tsx +53 -0
  27. package/src/Tabs.tsx +131 -0
  28. package/src/Timeline.tsx +33 -0
  29. package/src/Tooltip.tsx +355 -0
  30. package/src/Tree.tsx +246 -0
  31. package/src/Upload.tsx +10 -0
  32. package/src/form/Form.tsx +94 -0
  33. package/src/form/FormItem.tsx +139 -0
  34. package/src/form/context.ts +16 -0
  35. package/src/form/index.ts +13 -0
  36. package/src/hooks/createControllableValue.ts +68 -0
  37. package/src/hooks/createUpdateEffect.ts +16 -0
  38. package/src/hooks/index.ts +2 -0
  39. package/src/hooks/useClickAway.ts +18 -0
  40. package/src/hooks/useSize.ts +26 -0
  41. package/src/types/index.ts +5 -0
  42. package/src/utils/EventEmitter.ts +15 -0
  43. package/src/utils/ReactToSolid.tsx +38 -0
  44. package/src/utils/SolidToReact.tsx +27 -0
  45. package/src/utils/array.ts +21 -0
  46. package/src/utils/component.tsx +85 -0
  47. package/src/utils/solid.ts +53 -0
  48. package/src/utils/zh_CN.ts +236 -0
@@ -0,0 +1,139 @@
1
+ import {
2
+ type JSXElement,
3
+ type Component,
4
+ type JSX,
5
+ Show,
6
+ useContext,
7
+ createSignal,
8
+ onMount,
9
+ onCleanup,
10
+ } from 'solid-js'
11
+ import { get, isNil } from 'lodash-es'
12
+ import { Dynamic } from 'solid-js/web'
13
+ import { nanoid } from 'nanoid'
14
+ import cs from 'classnames'
15
+ import { type Schema } from 'yup'
16
+ import Context from './context'
17
+
18
+ export interface FormItemComponentProps<T = any> {
19
+ defaultValue?: T
20
+ status?: 'error' | 'warning'
21
+ onChange?: (value: T) => void
22
+ }
23
+
24
+ export interface FormItemProps {
25
+ class?: string
26
+ style?: JSX.CSSProperties
27
+ required?: boolean
28
+ label?: JSXElement
29
+ name?: string
30
+ initialValue?: any
31
+ rules?: Schema[]
32
+ component: Component<FormItemComponentProps>
33
+ }
34
+
35
+ const FormItem: Component<FormItemProps> = props => {
36
+ const { formInstance, rulesDict, setErrMsgDict, initialValues, setItemWidthDict, maxItemWidth } =
37
+ useContext(Context)
38
+ const [errMsg, setErrMsg] = createSignal('')
39
+ const id = nanoid()
40
+
41
+ onMount(() => {
42
+ if (isNil(props.name)) return
43
+
44
+ if (!isNil(props.initialValue)) {
45
+ formInstance.setFieldValue(props.name, props.initialValue)
46
+ }
47
+
48
+ if (!isNil(props.rules)) {
49
+ rulesDict[props.name] = props.rules
50
+ }
51
+
52
+ setErrMsgDict[props.name] = setErrMsg
53
+ })
54
+
55
+ let label: HTMLLabelElement
56
+ onMount(() => {
57
+ const resizeObserver = new ResizeObserver(entries => {
58
+ const [entry] = entries
59
+ // Firefox implements `borderBoxSize` as a single content rect, rather than an array
60
+ const borderBoxSize: ResizeObserverSize = Array.isArray(entry.borderBoxSize)
61
+ ? entry.borderBoxSize[0]
62
+ : entry.borderBoxSize
63
+ setItemWidthDict(dict => ({
64
+ ...dict,
65
+ [id]: borderBoxSize.inlineSize,
66
+ }))
67
+ })
68
+
69
+ resizeObserver.observe(label)
70
+
71
+ onCleanup(() => {
72
+ setItemWidthDict(dict => {
73
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
74
+ delete dict[id]
75
+ return { ...dict }
76
+ })
77
+ resizeObserver.disconnect()
78
+ })
79
+ })
80
+
81
+ const getLabel = (hidden?: boolean) => (
82
+ <label
83
+ class={cs(
84
+ 'ant-shrink-0 ant-h-32px ant-leading-32px not[:empty]:ant-pr-8px ant-text-right ant-[white-space:nowrap]',
85
+ hidden && 'ant-absolute ant-opacity-0',
86
+ )}
87
+ {...hidden ? {
88
+ ref: (el) => {
89
+ label = el
90
+ }
91
+ }: {
92
+ style: { width: `${maxItemWidth() ?? 0}px` }
93
+ }}
94
+ >
95
+ <Show when={!isNil(props.required)}>
96
+ <span class="ant-mr-4px ant-text-[var(--ant-color-error)]">*</span>
97
+ </Show>
98
+ <Show when={!isNil(props.label)}>
99
+ <label>{props.label}</label>
100
+ </Show>
101
+ </label>
102
+ )
103
+
104
+ return (
105
+ <div class={cs(props.class, 'ant-flex ant-items-center ant-mb-16px')} style={props.style}>
106
+ {/* 第一个 label 仅用于计算实际宽度 */}
107
+ {getLabel(true)}
108
+ {getLabel()}
109
+
110
+ <div class="ant-w-full">
111
+ <Dynamic
112
+ component={props.component}
113
+ defaultValue={props.initialValue ?? get(initialValues, props.name!)}
114
+ status={errMsg() ? 'error' : undefined}
115
+ onChange={(value: any) => {
116
+ if (!isNil(props.name)) formInstance.setFieldValue(props.name, value)
117
+
118
+ props.rules?.forEach(rule => {
119
+ rule
120
+ .validate(value)
121
+ .then(() => {
122
+ setErrMsg('')
123
+ })
124
+ .catch(err => {
125
+ setErrMsg(err.message)
126
+ })
127
+ })
128
+ }}
129
+ />
130
+
131
+ <Show when={errMsg()}>
132
+ <div class="ant-text-[var(--ant-color-error)]">{errMsg()}</div>
133
+ </Show>
134
+ </div>
135
+ </div>
136
+ )
137
+ }
138
+
139
+ export default FormItem
@@ -0,0 +1,16 @@
1
+ import { type Setter, createContext, type Accessor } from 'solid-js'
2
+ import { type FormInstance } from './Form'
3
+ import { type Schema } from 'yup'
4
+
5
+ const Context = createContext(
6
+ {} as {
7
+ formInstance: FormInstance
8
+ rulesDict: Record<string, Schema[]>
9
+ setErrMsgDict: Record<string, Setter<string>>
10
+ initialValues: {}
11
+ setItemWidthDict: Setter<Record<string, number>>
12
+ maxItemWidth: Accessor<number | undefined>
13
+ },
14
+ )
15
+
16
+ export default Context
@@ -0,0 +1,13 @@
1
+ import InternalForm from './Form'
2
+ import FormItem from './FormItem'
3
+
4
+ export type { FormInstance, FormProps } from './Form'
5
+ export type { FormItemProps, FormItemComponentProps } from './FormItem'
6
+
7
+ const Form = InternalForm as typeof InternalForm & {
8
+ Item: typeof FormItem
9
+ }
10
+
11
+ Form.Item = FormItem
12
+
13
+ export default Form
@@ -0,0 +1,68 @@
1
+ import { type Signal, createSignal } from 'solid-js'
2
+ import createUpdateEffect from './createUpdateEffect'
3
+
4
+ export interface Options<T> {
5
+ defaultValue?: T
6
+ defaultValuePropName?: string
7
+ valuePropName?: string
8
+ trigger?: string | undefined
9
+ }
10
+
11
+ export type Props = Record<string, any>
12
+
13
+ export interface StandardProps<T> {
14
+ value: T
15
+ defaultValue?: T
16
+ onChange: (val: T) => void
17
+ }
18
+
19
+ function createControllableValue<T = any>(props: StandardProps<T>): Signal<T>
20
+ function createControllableValue<T = any>(props: Props, options?: Options<T>): Signal<T>
21
+ function createControllableValue<T = any>(props: Props, options: Options<T> = {}) {
22
+ const {
23
+ defaultValuePropName = 'defaultValue',
24
+ valuePropName = 'value',
25
+ trigger = 'onChange',
26
+ } = options
27
+
28
+ const getValue = () => props[valuePropName] as T
29
+ // 为什么不使用 Object.hasOwn?
30
+ // solid 的 proxy 对象对于任何 key,都会返回 true
31
+ const isControlled = () => Object.keys(props).includes(valuePropName)
32
+
33
+ let defaultValue = options.defaultValue
34
+ if (isControlled()) {
35
+ defaultValue = getValue()
36
+ } else if (Object.keys(props).includes(defaultValuePropName)) {
37
+ defaultValue = props[defaultValuePropName]
38
+ }
39
+
40
+ const [value, _setValue] = createSignal(defaultValue)
41
+
42
+ createUpdateEffect(getValue, () => {
43
+ if (!isControlled()) return
44
+
45
+ _setValue(getValue() as any)
46
+ })
47
+
48
+ const setValue = (v: ((prev: T) => T) | T | undefined) => {
49
+ const newValue = typeof v === 'function'? (v as (prev: T) => T)(value()!) : v
50
+
51
+ if (!isControlled()) {
52
+ _setValue(newValue as any)
53
+ }
54
+
55
+ if (trigger) {
56
+ const onChange = props[trigger]
57
+ if (typeof onChange === 'function') {
58
+ onChange(newValue)
59
+ }
60
+ }
61
+
62
+ return newValue
63
+ }
64
+
65
+ return [value, setValue] as Signal<T>
66
+ }
67
+
68
+ export default createControllableValue
@@ -0,0 +1,16 @@
1
+ import { createEffect, on } from 'solid-js'
2
+ import type { Accessor, AccessorArray, NoInfer, OnEffectFunction } from 'solid-js'
3
+
4
+ /**
5
+ * 等同于 createEffect,但是会忽略首次执行,只在依赖更新时执行。
6
+ */
7
+ export default function createUpdateEffect<S, Next extends Prev, Prev = Next>(
8
+ deps: AccessorArray<S> | Accessor<S>,
9
+ fn: OnEffectFunction<S, undefined | NoInfer<Prev>, Next>,
10
+ ) {
11
+ createEffect(
12
+ on(deps, fn, {
13
+ defer: true,
14
+ }),
15
+ )
16
+ }
@@ -0,0 +1,2 @@
1
+ export { default as useClickAway } from './useClickAway'
2
+ export { default as useSize } from './useSize'
@@ -0,0 +1,18 @@
1
+ import { type Accessor, onCleanup } from 'solid-js'
2
+ import { toArray } from '../utils/array'
3
+
4
+ export default function useClickAway<T extends Event = Event>(
5
+ onClickAway: (event: T) => void,
6
+ target?: Accessor<Element | Element[]>,
7
+ ) {
8
+ const onClick = (event: Event) => {
9
+ const targets = target ? toArray(target()) : []
10
+ if (targets.every(item => !item.contains(event.target as Node))) {
11
+ onClickAway(event as T)
12
+ }
13
+ }
14
+ document.body.addEventListener('click', onClick)
15
+ onCleanup(() => {
16
+ document.body.removeEventListener('click', onClick)
17
+ })
18
+ }
@@ -0,0 +1,26 @@
1
+ import { Accessor, createSignal, onCleanup, onMount } from 'solid-js'
2
+
3
+ function getTarget(target: Element | Accessor<Element>) {
4
+ return target instanceof Element ? target : target()
5
+ }
6
+
7
+ export default function useSize(target: Element | Accessor<Element>) {
8
+ const [size, setSize] = createSignal<{
9
+ width: number
10
+ height: number
11
+ }>()
12
+
13
+ onMount(() => {
14
+ const _target = getTarget(target)
15
+ const ro = new ResizeObserver(() => {
16
+ setSize({
17
+ width: _target.clientWidth,
18
+ height: _target.clientHeight,
19
+ })
20
+ })
21
+ ro.observe(_target)
22
+ onCleanup(() => ro.disconnect())
23
+ })
24
+
25
+ return size
26
+ }
@@ -0,0 +1,5 @@
1
+ import { type JSXElement } from 'solid-js'
2
+
3
+ export type Key = string | number
4
+
5
+ export type StringOrJSXElement = string | number | undefined | null | (() => JSXElement)
@@ -0,0 +1,15 @@
1
+ type Subscription<T> = (val: T) => void
2
+
3
+ export default class EventEmitter<T> {
4
+ private readonly subscriptions = new Set<Subscription<T>>()
5
+
6
+ emit = (val: T) => {
7
+ for (const subscription of this.subscriptions) {
8
+ subscription(val)
9
+ }
10
+ }
11
+
12
+ subscription = (callback: Subscription<T>) => {
13
+ this.subscriptions.add(callback)
14
+ }
15
+ }
@@ -0,0 +1,38 @@
1
+ import React from 'react'
2
+ import { createRoot, type Root } from 'react-dom/client'
3
+ // import { renderToString } from 'react-dom/server'
4
+ import { onMount, onCleanup, createEffect, createMemo } from 'solid-js'
5
+
6
+ interface ReactToSolidProps<P extends {} = {}> {
7
+ component: React.FunctionComponent<P> | React.ComponentClass<P>
8
+ props: P
9
+ container?: Element
10
+ }
11
+
12
+ function ReactToSolid<P extends {} = {}>(props: ReactToSolidProps<P>) {
13
+ let root: Root
14
+
15
+ const rootEle = createMemo(
16
+ () => props.container ?? ((<div role={'ReactToSolid' as any} />) as Element),
17
+ )
18
+ onMount(() => {
19
+ root = createRoot(rootEle())
20
+ onCleanup(() => {
21
+ root.unmount()
22
+ })
23
+ })
24
+
25
+ createEffect(() => {
26
+ root.render(React.createElement(props.component, { ...props.props }))
27
+ })
28
+
29
+ // if (import.meta.env.SSR) {
30
+ // // eslint-disable-next-line solid/reactivity
31
+ // const node = React.createElement(props.component, { ...props.props })
32
+ // // eslint-disable-next-line solid/components-return-once, solid/no-innerhtml
33
+ // return <div innerHTML={renderToString(node)} />
34
+ // }
35
+ return <>{rootEle}</>
36
+ }
37
+
38
+ export default ReactToSolid
@@ -0,0 +1,27 @@
1
+ import React, { type FunctionComponent, type ReactElement, useEffect, useRef } from 'react'
2
+ import { type JSX } from 'solid-js'
3
+ import { render } from 'solid-js/web'
4
+
5
+ export interface SolidToReactProps {
6
+ children?: JSX.Element
7
+ container?: ReactElement
8
+ }
9
+
10
+ const SolidToReact: FunctionComponent<SolidToReactProps> = ({ children, container }) => {
11
+ const ref = useRef<HTMLDivElement>()
12
+
13
+ useEffect(() => render(() => children, ref.current!), [])
14
+
15
+ // if (import.meta.env.SSR) {
16
+ // return React.createElement('div', {
17
+ // dangerouslySetInnerHTML: { __html: renderToString(() => children) },
18
+ // })
19
+ // }
20
+ return container
21
+ ? React.cloneElement(container, {
22
+ ref,
23
+ })
24
+ : React.createElement('div', { ref, role: 'SolidToReact' })
25
+ }
26
+
27
+ export default SolidToReact
@@ -0,0 +1,21 @@
1
+ /**
2
+ * 如果传入一个非数组字段,则将其转化为数组
3
+ * @param value
4
+ * @returns
5
+ */
6
+ export function toArray<T>(value: T | T[]) {
7
+ return Array.isArray(value) ? value : [value]
8
+ }
9
+
10
+ export interface StandardNode {
11
+ children?: this[]
12
+ }
13
+ /**
14
+ * 将数组中每个对象的 children 拍扁后返回
15
+ * @param nodes
16
+ * @returns
17
+ */
18
+ export function flatChildren<T extends StandardNode = StandardNode>(nodes: T[] | undefined): T[] {
19
+ if (!nodes) return []
20
+ return nodes.flatMap(node => [node, ...flatChildren(node.children)])
21
+ }
@@ -0,0 +1,85 @@
1
+ import React from 'react'
2
+ import { type Component, createMemo, type JSXElement } from 'solid-js'
3
+ import { omit } from 'lodash-es'
4
+ import { solidToReact } from './solid'
5
+ import ReactToSolid from './ReactToSolid'
6
+ import { ConfigProvider } from 'antd'
7
+ import zhCN from './zh_CN'
8
+ import { type ConfigProviderProps } from 'antd/es/config-provider'
9
+
10
+ /**
11
+ * 将组件 props 中的 className 替换为 class
12
+ * @param C
13
+ * @returns
14
+ */
15
+ export function replaceClassName<
16
+ T extends { className?: string },
17
+ Target extends Omit<T, 'className'> & { class?: string },
18
+ >(C: Component<T>): Component<Target> {
19
+ return function (_props: Target) {
20
+ const props = createMemo(() => {
21
+ return {
22
+ ...omit(_props, 'class'),
23
+ className: _props.class,
24
+ }
25
+ })
26
+ return <C {...(props() as unknown as T)} />
27
+ }
28
+ }
29
+
30
+ /**
31
+ * 将组件 props 中的 children 替换为 JSXElement
32
+ * @param C
33
+ * @returns
34
+ */
35
+ export function replaceChildren<
36
+ T extends { children?: React.ReactNode },
37
+ Target extends Omit<T, 'children'> & { children?: JSXElement },
38
+ >(C: Component<T>): Component<Target> {
39
+ return function (_props: Target) {
40
+ const props = createMemo(() => {
41
+ return {
42
+ ..._props,
43
+ children: solidToReact(_props.children),
44
+ }
45
+ })
46
+ return <C {...(props() as unknown as T)} />
47
+ }
48
+ }
49
+
50
+ export function reactToSolidComponent<P extends {} = {}>(
51
+ component: React.FunctionComponent<P> | React.ComponentClass<P>,
52
+ container?: Element | (() => Element),
53
+ ) {
54
+ return function (props: P) {
55
+ return (
56
+ <ReactToSolid
57
+ component={component}
58
+ props={props}
59
+ container={typeof container === 'function' ? container() : container}
60
+ />
61
+ )
62
+ }
63
+ }
64
+
65
+ /**
66
+ * 返回被 ConfigProvider 包裹后的 component
67
+ * @param component
68
+ * @param configProviderProps
69
+ * @returns
70
+ */
71
+ export function configProvider<P extends {} = {}>(
72
+ component: React.FunctionComponent<P> | React.ComponentClass<P>,
73
+ configProviderProps?: ConfigProviderProps,
74
+ ) {
75
+ return function (props: P) {
76
+ return React.createElement(
77
+ ConfigProvider,
78
+ {
79
+ locale: zhCN,
80
+ ...configProviderProps,
81
+ },
82
+ React.createElement(component, props),
83
+ )
84
+ }
85
+ }
@@ -0,0 +1,53 @@
1
+ import React from 'react'
2
+ import { type JSXElement, type JSX } from 'solid-js'
3
+ import { isNil } from 'lodash-es'
4
+ import SolidToReact from './SolidToReact'
5
+ import { type StringOrJSXElement } from '../types'
6
+
7
+ /**
8
+ * 判断 JSXElement 是否是基础类型
9
+ * @param value JSXElement
10
+ * @returns
11
+ */
12
+ export function isBaseType(
13
+ value: JSXElement,
14
+ ): value is string | number | boolean | null | undefined {
15
+ return (
16
+ typeof value === 'string' ||
17
+ typeof value === 'number' ||
18
+ typeof value === 'boolean' ||
19
+ value === null ||
20
+ value === undefined
21
+ )
22
+ }
23
+
24
+ export function solidToReact(children: JSXElement) {
25
+ return isBaseType(children)
26
+ ? children
27
+ : React.createElement(SolidToReact, {
28
+ children,
29
+ })
30
+ }
31
+
32
+ export function dispatchEventHandlerUnion<T, E extends Event>(
33
+ handler: JSX.EventHandlerUnion<T, E> | undefined,
34
+ e: E & {
35
+ currentTarget: T
36
+ target: Element
37
+ },
38
+ ) {
39
+ if (isNil(handler)) {
40
+ return
41
+ }
42
+
43
+ if (typeof handler === 'function') {
44
+ handler(e)
45
+ return
46
+ }
47
+
48
+ handler[0](handler[1], e)
49
+ }
50
+
51
+ export function unwrapStringOrJSXElement(value: StringOrJSXElement): JSXElement {
52
+ return typeof value === 'function' ? value() : value
53
+ }