intable 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.
- package/README.md +1 -2
- package/docs/index-BaMALNy6.css +1 -0
- package/docs/index-CDN48t9E.js +3 -0
- package/docs/index-Cc4RNkLY.css +1 -0
- package/docs/index-MRnbkYmU.js +3 -0
- package/docs/index.html +15 -0
- package/docs/vite.svg +1 -0
- package/index.html +14 -0
- package/package.json +30 -37
- package/packages/intable/README.md +379 -0
- package/packages/intable/package.json +51 -0
- package/packages/intable/src/assets/ClearFormat.svg +3 -0
- package/packages/intable/src/assets/Forms.svg +4 -0
- package/packages/intable/src/assets/MergeCell.svg +4 -0
- package/packages/intable/src/assets/SplitCell.svg +4 -0
- package/packages/intable/src/assets/gap.svg +3 -0
- package/packages/intable/src/assets/loading.svg +12 -0
- package/packages/intable/src/assets/paint.svg +9 -0
- package/packages/intable/src/assets/solid.svg +1 -0
- package/packages/intable/src/components/Columns.tsx +86 -0
- package/packages/intable/src/components/DocTree.tsx +36 -0
- package/packages/intable/src/components/Menu.tsx +109 -0
- package/packages/intable/src/components/Popover.tsx +55 -0
- package/packages/intable/src/components/RecycleList.tsx +99 -0
- package/packages/intable/src/components/Render.tsx +26 -0
- package/packages/intable/src/components/Split.tsx +56 -0
- package/packages/intable/src/components/Tree.tsx +115 -0
- package/packages/intable/src/components/utils.tsx +12 -0
- package/packages/intable/src/hooks/index.ts +200 -0
- package/packages/intable/src/hooks/useDir.ts +78 -0
- package/packages/intable/src/hooks/useSelector.ts +91 -0
- package/packages/intable/src/hooks/useSort.tsx +118 -0
- package/packages/intable/src/hooks/useVirtualizer.ts +180 -0
- package/packages/intable/src/index.tsx +481 -0
- package/packages/intable/src/plugins/CellChangeHighlightPlugin.tsx +5 -0
- package/packages/intable/src/plugins/CellMergePlugin.tsx +153 -0
- package/packages/intable/src/plugins/CellSelectionPlugin.tsx +175 -0
- package/packages/intable/src/plugins/CommandPlugin.tsx +74 -0
- package/packages/intable/src/plugins/CopyPastePlugin.tsx +63 -0
- package/packages/intable/src/plugins/DiffPlugin.tsx +107 -0
- package/packages/intable/src/plugins/DragPlugin.tsx +81 -0
- package/packages/intable/src/plugins/EditablePlugin.tsx +252 -0
- package/packages/intable/src/plugins/ExpandPlugin.tsx +80 -0
- package/packages/intable/src/plugins/HeaderGroup.tsx +289 -0
- package/packages/intable/src/plugins/HistoryPlugin.tsx +49 -0
- package/packages/intable/src/plugins/MenuPlugin.tsx +195 -0
- package/packages/intable/src/plugins/RenderPlugin/components.tsx +51 -0
- package/packages/intable/src/plugins/RenderPlugin/index.tsx +81 -0
- package/packages/intable/src/plugins/ResizePlugin.tsx +122 -0
- package/packages/intable/src/plugins/RowGroupPlugin.tsx +122 -0
- package/packages/intable/src/plugins/RowSelectionPlugin.tsx +65 -0
- package/packages/intable/src/plugins/TreePlugin.tsx +212 -0
- package/packages/intable/src/plugins/VirtualScrollPlugin.tsx +190 -0
- package/packages/intable/src/plugins/ZodValidatorPlugin.tsx +61 -0
- package/packages/intable/src/style.scss +244 -0
- package/{dist → packages/intable/src}/theme/antd.scss +14 -5
- package/{dist → packages/intable/src}/theme/element-plus.scss +6 -5
- package/packages/intable/src/tree.ts +13 -0
- package/packages/intable/src/types/auto-imports.d.ts +13 -0
- package/packages/intable/src/utils.ts +122 -0
- package/packages/intable/src/wc.tsx +35 -0
- package/packages/intable/src/web-component.ts +1 -0
- package/packages/react/package.json +31 -0
- package/packages/react/src/index.ts +44 -0
- package/packages/react/src/plugins/antd.ts +94 -0
- package/packages/react/src/style.scss +12 -0
- package/packages/react/src/types/auto-imports.d.ts +10 -0
- package/packages/vue/package.json +34 -0
- package/packages/vue/src/index.ts +63 -0
- package/packages/vue/src/plugins/element-plus.ts +69 -0
- package/packages/vue/src/style.scss +12 -0
- package/packages/vue/src/types/auto-imports.d.ts +10 -0
- package/pnpm-workspace.yaml +2 -0
- package/public/vite.svg +1 -0
- package/scripts/build.js +184 -0
- package/scripts/publish.js +95 -0
- package/src/assets/ClearFormat.svg +3 -0
- package/src/assets/Forms.svg +4 -0
- package/src/assets/MergeCell.svg +4 -0
- package/src/assets/SplitCell.svg +4 -0
- package/src/assets/gap.svg +3 -0
- package/src/assets/loading.svg +12 -0
- package/src/assets/paint.svg +9 -0
- package/src/assets/solid.svg +1 -0
- package/src/demo-vue.ts +54 -0
- package/src/demo.tsx +107 -0
- package/src/index.scss +105 -0
- package/src/styles/index.scss +172 -0
- package/src/types/auto-imports.d.ts +13 -0
- package/stats.html +4949 -0
- package/tsconfig.app.json +34 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +26 -0
- package/vite.config.ts +63 -0
- package/dist/__uno.css +0 -1
- package/dist/chevron-right.js +0 -6
- package/dist/components/Columns.d.ts +0 -3
- package/dist/components/Columns.js +0 -71
- package/dist/components/DocTree.d.ts +0 -4
- package/dist/components/DocTree.js +0 -32
- package/dist/components/Menu.d.ts +0 -1
- package/dist/components/Menu.js +0 -107
- package/dist/components/Popover.d.ts +0 -14
- package/dist/components/Popover.js +0 -41
- package/dist/components/Render.d.ts +0 -4
- package/dist/components/Render.js +0 -20
- package/dist/components/Split.d.ts +0 -15
- package/dist/components/Split.js +0 -76
- package/dist/components/Tree.d.ts +0 -37
- package/dist/components/Tree.js +0 -82
- package/dist/components/utils.d.ts +0 -3
- package/dist/components/utils.js +0 -8
- package/dist/hooks/index.d.ts +0 -40
- package/dist/hooks/index.js +0 -157
- package/dist/hooks/useDir.d.ts +0 -11
- package/dist/hooks/useDir.js +0 -42
- package/dist/hooks/useSelector.d.ts +0 -16
- package/dist/hooks/useSelector.js +0 -35
- package/dist/hooks/useSort.d.ts +0 -18
- package/dist/hooks/useSort.js +0 -83
- package/dist/hooks/useVirtualizer.d.ts +0 -25
- package/dist/hooks/useVirtualizer.js +0 -67
- package/dist/index.d.ts +0 -130
- package/dist/index.js +0 -347
- package/dist/loading.js +0 -6
- package/dist/plugins/CellChangeHighlightPlugin.d.ts +0 -2
- package/dist/plugins/CellChangeHighlightPlugin.js +0 -4
- package/dist/plugins/CellMergePlugin.d.ts +0 -12
- package/dist/plugins/CellMergePlugin.js +0 -2
- package/dist/plugins/CellSelectionPlugin.d.ts +0 -15
- package/dist/plugins/CellSelectionPlugin.js +0 -115
- package/dist/plugins/CommandPlugin.d.ts +0 -14
- package/dist/plugins/CommandPlugin.js +0 -12
- package/dist/plugins/CopyPastePlugin.d.ts +0 -14
- package/dist/plugins/CopyPastePlugin.js +0 -42
- package/dist/plugins/DiffPlugin.d.ts +0 -23
- package/dist/plugins/DiffPlugin.js +0 -56
- package/dist/plugins/DragPlugin.d.ts +0 -14
- package/dist/plugins/DragPlugin.js +0 -47
- package/dist/plugins/EditablePlugin.d.ts +0 -48
- package/dist/plugins/EditablePlugin.js +0 -141
- package/dist/plugins/ExpandPlugin.d.ts +0 -18
- package/dist/plugins/ExpandPlugin.js +0 -50
- package/dist/plugins/HistoryPlugin.d.ts +0 -10
- package/dist/plugins/HistoryPlugin.js +0 -30
- package/dist/plugins/MenuPlugin.d.ts +0 -18
- package/dist/plugins/MenuPlugin.js +0 -107
- package/dist/plugins/RenderPlugin/components.d.ts +0 -5
- package/dist/plugins/RenderPlugin/components.js +0 -87
- package/dist/plugins/RenderPlugin/index.d.ts +0 -30
- package/dist/plugins/RenderPlugin/index.js +0 -49
- package/dist/plugins/ResizePlugin.d.ts +0 -27
- package/dist/plugins/ResizePlugin.js +0 -81
- package/dist/plugins/RowGroupPlugin.d.ts +0 -17
- package/dist/plugins/RowGroupPlugin.js +0 -83
- package/dist/plugins/RowSelectionPlugin.d.ts +0 -20
- package/dist/plugins/RowSelectionPlugin.js +0 -42
- package/dist/plugins/VirtualScrollPlugin.d.ts +0 -15
- package/dist/plugins/VirtualScrollPlugin.js +0 -96
- package/dist/plus.js +0 -6
- package/dist/style.css +0 -3
- package/dist/types/auto-imports.d.js +0 -0
- package/dist/utils.d.ts +0 -30
- package/dist/utils.js +0 -70
- package/dist/wc.d.ts +0 -1
- package/dist/wc.js +0 -21
- package/dist/web-component.d.ts +0 -1
- package/dist/web-component.js +0 -2
- package/dist/x.js +0 -6
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { $PROXY, batch, createComputed, createEffect, createMemo, createRenderEffect, createRoot, createSignal, mergeProps, on, onCleanup, untrack, type Signal } from 'solid-js'
|
|
2
|
+
import { $RAW, createMutable } from 'solid-js/store'
|
|
3
|
+
import { createEventListener, createEventListenerMap } from '@solid-primitives/event-listener'
|
|
4
|
+
import { createPointerListeners } from '@solid-primitives/pointer'
|
|
5
|
+
import { access, type Many, type MaybeAccessor, } from '@solid-primitives/utils'
|
|
6
|
+
import { makePersisted, storageSync } from '@solid-primitives/storage'
|
|
7
|
+
import { createMutationObserver } from '@solid-primitives/mutation-observer'
|
|
8
|
+
import { isFunction, isPromise, mapValues } from 'es-toolkit'
|
|
9
|
+
import { castArray } from 'es-toolkit/compat'
|
|
10
|
+
import { createKeybindingsHandler } from 'tinykeys'
|
|
11
|
+
import { unFn } from '../utils'
|
|
12
|
+
|
|
13
|
+
interface UseMoveOptions {
|
|
14
|
+
preventDefault?: boolean
|
|
15
|
+
start?(
|
|
16
|
+
e: PointerEvent,
|
|
17
|
+
move: (cb: MoveCB) => void,
|
|
18
|
+
end: (cb: EndCb) => void
|
|
19
|
+
): void,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type MoveCB = (e: PointerEvent, o: { sx: number, sy: number, ox: number, oy: number }) => void
|
|
23
|
+
type EndCb = (e: PointerEvent) => void
|
|
24
|
+
|
|
25
|
+
export function usePointerDrag(el: MaybeAccessor<HTMLElement | undefined>, options: UseMoveOptions) {
|
|
26
|
+
options = mergeProps({ preventDefault: true } as UseMoveOptions, options)
|
|
27
|
+
|
|
28
|
+
createPointerListeners({
|
|
29
|
+
target: el,
|
|
30
|
+
passive: false,
|
|
31
|
+
onDown(e) {
|
|
32
|
+
options.preventDefault && e.preventDefault()
|
|
33
|
+
const [sx, sy] = [e.x, e.y]
|
|
34
|
+
|
|
35
|
+
let move: MoveCB | void
|
|
36
|
+
let end: EndCb | void
|
|
37
|
+
options.start?.(e, (cb) => move = cb, (cb) => end = cb)
|
|
38
|
+
|
|
39
|
+
createRoot(dispose => {
|
|
40
|
+
createPointerListeners({
|
|
41
|
+
target: document,
|
|
42
|
+
onMove(e) {
|
|
43
|
+
const [ox, oy] = [e.x - sx, e.y - sy]
|
|
44
|
+
move?.(e, { sx, sy, ox, oy })
|
|
45
|
+
},
|
|
46
|
+
onUp() {
|
|
47
|
+
end?.(e)
|
|
48
|
+
dispose()
|
|
49
|
+
move = void 0
|
|
50
|
+
end = void 0
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
// fix: does't cb onUp when drop
|
|
54
|
+
createEventListenerMap(document, {
|
|
55
|
+
drop: dispose
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function model(el: any, value: () => [() => string, (v: string) => any]) {
|
|
63
|
+
const [field, setField] = value()
|
|
64
|
+
createRenderEffect(() => (el.value = field()))
|
|
65
|
+
el.addEventListener("input", (e) => setField((e.target as HTMLInputElement).value))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function toSignle<T extends Record<string, any>>(state: T, k: keyof T) {
|
|
69
|
+
return [() => state[k], v => state[k] = v]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function useDark() {
|
|
73
|
+
const get = v => v == 'dark' || (prefersDark() && !v), set = v => v ? 'dark' : 'light'
|
|
74
|
+
const prefersDark = () => window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
75
|
+
const dark = makePersisted(createSignal(prefersDark()), { name: 'color-schema', storage: localStorage, sync: storageSync, serialize: set, deserialize: get })
|
|
76
|
+
createEffect(() => document.documentElement.classList[dark[0]() ? 'add' : 'remove']('dark'))
|
|
77
|
+
createEffect(() => window.dispatchEvent(new StorageEvent('storage', { key: 'color-schema', newValue: set(dark[0]()) })))
|
|
78
|
+
return dark
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function useMemoAsync<T>(fn: (prev?: T) => Promise<T> | T, init?: Awaited<T>) {
|
|
82
|
+
const REJECT = Symbol()
|
|
83
|
+
const [val, setVal] = createSignal(init)
|
|
84
|
+
createComputed(async () => {
|
|
85
|
+
const ret = fn(untrack(val))
|
|
86
|
+
const v = ret instanceof Promise ? await new Promise((resolve) => {
|
|
87
|
+
ret.then(resolve)
|
|
88
|
+
onCleanup(() => resolve(REJECT))
|
|
89
|
+
}) : ret
|
|
90
|
+
v == REJECT || setVal(() => v)
|
|
91
|
+
})
|
|
92
|
+
return val
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function useSignle2<T>(v: T | (() => T), opt?: { before?: (v: T) => Promise<T | void> | T }) {
|
|
96
|
+
const state = createSignal(isFunction(v) ? void 0 : v)
|
|
97
|
+
const before = v => {
|
|
98
|
+
const v2 = opt?.before?.(v)
|
|
99
|
+
return isPromise(v2) ? v2.then(v3 => v3 === void 0 ? v : v3) : v2 ?? v
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const val = useMemoAsync(() => before(state[0]() as T))
|
|
103
|
+
|
|
104
|
+
if (isFunction(v)) {
|
|
105
|
+
const fned = useMemoAsync(() => before(v()))
|
|
106
|
+
createComputed(() => state[1](fned()))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return [val, state[1]] as Signal<T>
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const $MEMO = Symbol()
|
|
113
|
+
|
|
114
|
+
type Reactive<T extends object> = { [K in keyof T]: T[K] extends () => infer V ? V : T[K] }
|
|
115
|
+
export function toReactive<T extends object>(fn: (() => T) | T): Reactive<T> {
|
|
116
|
+
const v = () => unFn(fn)
|
|
117
|
+
const trueFn = () => true
|
|
118
|
+
const get = k => (e => typeof e == 'function' && $MEMO in e ? e() : e)(v()[k])
|
|
119
|
+
return new Proxy(Object.create(null), {
|
|
120
|
+
get: (o, k, r) => k == $PROXY ? r : k == $RAW ? r : (v => typeof v == 'function' && $MEMO in v ? v() : v)(v()[k]),
|
|
121
|
+
set: trueFn,
|
|
122
|
+
defineProperty: (o, k, attributes) => Object.defineProperty(v(), k, attributes),
|
|
123
|
+
deleteProperty: trueFn,
|
|
124
|
+
getPrototypeOf: () => Object.getPrototypeOf(v()),
|
|
125
|
+
has: (o, p) => p == $PROXY || p in v(),
|
|
126
|
+
ownKeys: (o) => Object.keys(v()),
|
|
127
|
+
getOwnPropertyDescriptor: (o, k) => ({ enumerable: true, configurable: true, get() { return get(k) }, set: trueFn }),
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function useMemo<T>(fn: () => T) {
|
|
132
|
+
const ret = createMemo(fn)
|
|
133
|
+
ret[$MEMO] = 1
|
|
134
|
+
return ret
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function useHover(el: MaybeAccessor<Many<HTMLElement | undefined>>) {
|
|
138
|
+
const [hover, setHover] = createSignal(false)
|
|
139
|
+
createEventListener(el, 'pointerenter', () => setHover(true))
|
|
140
|
+
createEventListener(el, 'pointerleave', () => setHover(false))
|
|
141
|
+
return hover
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function useMouseDown(el: MaybeAccessor<Many<HTMLElement | undefined>>) {
|
|
145
|
+
const [down, setDown] = createSignal(false)
|
|
146
|
+
createEventListener(el, 'pointerdown', () => setDown(true))
|
|
147
|
+
createEventListener(document.body, 'pointerup', () => setDown(false))
|
|
148
|
+
return down
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function useClicked(el: MaybeAccessor<Many<HTMLElement | undefined>>) {
|
|
152
|
+
const [clicked, setClicked] = createSignal(false)
|
|
153
|
+
const els = () => castArray(access(el))
|
|
154
|
+
createEventListener(() => els().map(e => e?.getRootNode()), 'click', e => setClicked(els().some(el => el?.contains(e.target))))
|
|
155
|
+
return clicked
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function useMutation<T>(initial: MaybeAccessor<Node | Node[]>, options: MutationObserverInit, cb: () => T) {
|
|
159
|
+
const ret = createSignal<T>(cb())
|
|
160
|
+
createMutationObserver(initial, options, ms => ret[1](cb() as any))
|
|
161
|
+
return ret[0]
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function useTinykeys(el: MaybeAccessor<HTMLElement | undefined>, handlers) {
|
|
165
|
+
createEventListener(el, 'keydown', createKeybindingsHandler({
|
|
166
|
+
...mapValues(handlers, cb => e => {
|
|
167
|
+
e.preventDefault()
|
|
168
|
+
cb(e)
|
|
169
|
+
})
|
|
170
|
+
}))
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function useHistory([val, setVal]) {
|
|
174
|
+
let bool = 1
|
|
175
|
+
const state = createMutable({ index: -1, history: [] as any[] })
|
|
176
|
+
const clear = () => (state.index = 0, state.history = [val()])
|
|
177
|
+
const canUndo = () => state.index > 0
|
|
178
|
+
const canRedo = () => state.index < state.history.length - 1
|
|
179
|
+
const undo = () => canUndo() && ((bool = 0) || setVal(state.history[--state.index]))
|
|
180
|
+
const redo = () => canRedo() && ((bool = 0) || setVal(state.history[++state.index]))
|
|
181
|
+
createRenderEffect(on(val, ret => {
|
|
182
|
+
if (ret == null) return // todo
|
|
183
|
+
if (!bool) return bool = 1
|
|
184
|
+
if (canRedo()) state.history = state.history.slice(0, state.index + 1)
|
|
185
|
+
state.history[++state.index] = ret
|
|
186
|
+
}))
|
|
187
|
+
return { undo, redo, clear, get index() { return state.index }, get history() { return state.history } }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function useMemoState(fn) {
|
|
191
|
+
const state = createMutable({})
|
|
192
|
+
createComputed(() => {
|
|
193
|
+
const val = fn()
|
|
194
|
+
untrack(() => batch(() => {
|
|
195
|
+
for (const k in state) k in val || (delete state[k])
|
|
196
|
+
Object.assign(state, val)
|
|
197
|
+
}))
|
|
198
|
+
})
|
|
199
|
+
return state
|
|
200
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createEffect, mergeProps } from 'solid-js'
|
|
2
|
+
import { createEventListener } from '@solid-primitives/event-listener'
|
|
3
|
+
import { createPointerList } from '@solid-primitives/pointer'
|
|
4
|
+
import { access, type MaybeAccessor } from '@solid-primitives/utils'
|
|
5
|
+
import { createMutationObserver } from '@solid-primitives/mutation-observer'
|
|
6
|
+
|
|
7
|
+
interface UseDirOptions {
|
|
8
|
+
ref: MaybeAccessor<HTMLElement | undefined>,
|
|
9
|
+
list?: MaybeAccessor<HTMLElement | undefined>,
|
|
10
|
+
loop?: boolean
|
|
11
|
+
options?: EventListenerOptions
|
|
12
|
+
defaultFirst?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function useDir(options: UseDirOptions) {
|
|
16
|
+
options = mergeProps({ loop: true }, options)
|
|
17
|
+
const list = () => access(options.list) ?? access(options.ref)
|
|
18
|
+
const ref = () => access(options.ref) ?? access(options.list)
|
|
19
|
+
|
|
20
|
+
createEffect(() => {
|
|
21
|
+
if (ref().tabIndex > -1) return
|
|
22
|
+
ref().tabIndex = 0
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
createEventListener(ref, 'keydown', e => {
|
|
26
|
+
if (!['ArrowDown', 'ArrowUp', 'Enter'].includes(e.key)) return
|
|
27
|
+
|
|
28
|
+
e.stopPropagation()
|
|
29
|
+
e.preventDefault()
|
|
30
|
+
|
|
31
|
+
let hover = list()?.querySelector('.hover')
|
|
32
|
+
|
|
33
|
+
if (e.key == 'Enter') {
|
|
34
|
+
hover?.click()
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let index = (
|
|
39
|
+
e.key == 'ArrowDown' ? hover ? +hover?.getAttribute('data-index')! + 1 : 0 :
|
|
40
|
+
e.key == 'ArrowUp' ? hover ? +hover?.getAttribute('data-index')! - 1 : -1 :
|
|
41
|
+
0
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
const el = list()?.querySelector(`[data-index='${index}']`)
|
|
45
|
+
if (!el) {
|
|
46
|
+
if (options.loop) {
|
|
47
|
+
hover?.classList.remove('hover')
|
|
48
|
+
index >= 0
|
|
49
|
+
? list()?.querySelector(`[data-index='0']`)?.classList.add('hover')
|
|
50
|
+
: [...list()?.querySelectorAll(`[data-index]`)].at(-1)?.classList.add('hover')
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
// list()?.querySelector(`[data-index=0]`)!
|
|
54
|
+
hover?.classList.remove('hover')
|
|
55
|
+
el.classList.add('hover')
|
|
56
|
+
}
|
|
57
|
+
}, options.options)
|
|
58
|
+
|
|
59
|
+
createEffect(() => {
|
|
60
|
+
if (!options.defaultFirst) return
|
|
61
|
+
const el = list()
|
|
62
|
+
if (!el) return
|
|
63
|
+
const hover = () => {
|
|
64
|
+
if (el.querySelector('.hover')) return
|
|
65
|
+
el.querySelector(`[data-index]`)?.classList.add('hover')
|
|
66
|
+
}
|
|
67
|
+
createMutationObserver(el, { childList: true }, hover)
|
|
68
|
+
hover()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
createEventListener(list, 'mouseover', () => {
|
|
72
|
+
list()?.querySelector('.hover')?.classList.remove('hover')
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function VDir(el: HTMLElement, options) {
|
|
77
|
+
useDir({ ...options(), list: el })
|
|
78
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createSignal, createMemo, createSelector } from 'solid-js'
|
|
2
|
+
import { log, toArr } from '../utils'
|
|
3
|
+
|
|
4
|
+
interface UseSelectorOpt<T> {
|
|
5
|
+
value?: T
|
|
6
|
+
onChange?: (v: T) => void
|
|
7
|
+
multiple?: boolean
|
|
8
|
+
selectable?: (v) => boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class SingleSet implements Set<any> {
|
|
12
|
+
#value: any
|
|
13
|
+
constructor(value) { this.#value = Array.from(value || [])[0] }
|
|
14
|
+
add(value) { this.#value = value; return this }
|
|
15
|
+
clear() { this.#value = undefined }
|
|
16
|
+
delete(value) { if (this.#value === value) { this.#value = undefined; return true } return false }
|
|
17
|
+
forEach(callbackfn) { if (this.#value !== undefined) callbackfn.call(this, this.#value, this.#value, this) }
|
|
18
|
+
has(value) { return this.#value === value }
|
|
19
|
+
get size() { return this.#value !== undefined ? 1 : 0 }
|
|
20
|
+
entries() { return this.values() }
|
|
21
|
+
keys() { return this.values() }
|
|
22
|
+
values() { return this.#value !== undefined ? [[this.#value, this.#value]].entries() : [].entries() }
|
|
23
|
+
[Symbol.iterator]() { return this.values()[Symbol.iterator]() }
|
|
24
|
+
[Symbol.toStringTag] = 'SingleSet'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function useSelector<T = any>(opt: UseSelectorOpt<T>) {
|
|
28
|
+
const { value: initialValue, onChange, multiple = false, selectable } = opt
|
|
29
|
+
|
|
30
|
+
const Set2 = (multiple ? Set : SingleSet) as SetConstructor
|
|
31
|
+
|
|
32
|
+
const [selected, setSelected] = createSignal(new Set2(toArr(initialValue)))
|
|
33
|
+
|
|
34
|
+
// 检查是否包含某个值
|
|
35
|
+
const has = createSelector<Set<any>, any>(selected, (a, b) => b.has(a as T))
|
|
36
|
+
|
|
37
|
+
// 检查值是否可选择
|
|
38
|
+
const isSelectable = (v: T): boolean => {
|
|
39
|
+
return selectable ? selectable(v) : true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 清空选择
|
|
43
|
+
const clear = () => {
|
|
44
|
+
setSelected(new Set2())
|
|
45
|
+
onChange?.(value())
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 设置选择
|
|
49
|
+
const set = (v: T) => {
|
|
50
|
+
if (!isSelectable(v)) return
|
|
51
|
+
setSelected(new Set2(toArr(v)))
|
|
52
|
+
onChange?.(value())
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 添加选择
|
|
56
|
+
const add = (v: T) => {
|
|
57
|
+
if (!isSelectable(v)) return
|
|
58
|
+
const newSet = new Set2(selected())
|
|
59
|
+
newSet.add(v)
|
|
60
|
+
setSelected(newSet)
|
|
61
|
+
onChange?.(value())
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 删除选择
|
|
65
|
+
const del = (v: T) => {
|
|
66
|
+
const newSet = new Set2(selected())
|
|
67
|
+
newSet.delete(v)
|
|
68
|
+
setSelected(newSet)
|
|
69
|
+
onChange?.(value())
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 切换选择状态
|
|
73
|
+
const toggle = (v: T) => {
|
|
74
|
+
has(v) ? del(v) : add(v)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 使用 createMemo 优化 selected 的计算
|
|
78
|
+
const value = createMemo(() => {
|
|
79
|
+
return (multiple ? Array.from(selected()) : Array.from(selected())[0]) as T
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
clear,
|
|
84
|
+
set,
|
|
85
|
+
has,
|
|
86
|
+
add,
|
|
87
|
+
del,
|
|
88
|
+
toggle,
|
|
89
|
+
get value() { return value() }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { createMutable, reconcile } from 'solid-js/store'
|
|
2
|
+
import { mergeProps, Portal } from 'solid-js/web'
|
|
3
|
+
|
|
4
|
+
import { createEventListener, createEventListenerMap } from '@solid-primitives/event-listener'
|
|
5
|
+
import { access, type MaybeAccessor } from '@solid-primitives/utils'
|
|
6
|
+
import { findAsync, log } from '../utils'
|
|
7
|
+
|
|
8
|
+
type Awaitable<T> = T | Promise<T>;
|
|
9
|
+
|
|
10
|
+
interface UseSortOption {
|
|
11
|
+
enable?: boolean
|
|
12
|
+
guideLine: any
|
|
13
|
+
draggable: (el: HTMLElement) => Awaitable<boolean>
|
|
14
|
+
dragover: (el: HTMLElement) => boolean
|
|
15
|
+
children: (el: HTMLElement) => HTMLElement[]
|
|
16
|
+
dragend: () => void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const useSort = (el: MaybeAccessor<HTMLElement | undefined>, opt: UseSortOption) => {
|
|
20
|
+
opt = mergeProps({ enable: true } as UseSortOption, opt)
|
|
21
|
+
let count = 0
|
|
22
|
+
|
|
23
|
+
createEventListenerMap(() => opt.enable ? access(el) : void 0, {
|
|
24
|
+
async pointerdown(e) {
|
|
25
|
+
const _c = count
|
|
26
|
+
const drag = await findAsync(e.composedPath(), e => e instanceof HTMLElement && opt.draggable(e))
|
|
27
|
+
if (_c != count) return
|
|
28
|
+
if (!drag) return
|
|
29
|
+
e.stopPropagation()
|
|
30
|
+
state.drag = drag
|
|
31
|
+
state.drag?.setAttribute('draggable', 'true')
|
|
32
|
+
},
|
|
33
|
+
pointerup() {
|
|
34
|
+
count++
|
|
35
|
+
dragend()
|
|
36
|
+
},
|
|
37
|
+
pointermove() {
|
|
38
|
+
count++
|
|
39
|
+
},
|
|
40
|
+
dragstart(e) {
|
|
41
|
+
e.dataTransfer!.setDragImage(document.createElement('img'), 0, 0)
|
|
42
|
+
},
|
|
43
|
+
dragover(e) {
|
|
44
|
+
if (!state.drag) return
|
|
45
|
+
const aa = e.composedPath().filter(e => e instanceof HTMLElement)
|
|
46
|
+
const over = state.over = aa.find(e => opt.dragover(e)) ?? state.over
|
|
47
|
+
if (!over) return
|
|
48
|
+
e.preventDefault()
|
|
49
|
+
e.stopPropagation()
|
|
50
|
+
//
|
|
51
|
+
const children = opt.children(over) as HTMLElement[]
|
|
52
|
+
if (children) {
|
|
53
|
+
let i = 0, d = Infinity, s = ''
|
|
54
|
+
children.forEach((el, ii) => {
|
|
55
|
+
const display = getComputedStyle(el).display
|
|
56
|
+
const __ = ['table-cell', 'inline'].some(e => display.includes(e))
|
|
57
|
+
const rect = el.getBoundingClientRect()
|
|
58
|
+
if (__) {
|
|
59
|
+
let dd = Math.sqrt((e.clientX - rect.x) ** 2 + (e.clientY - rect.y + rect.height / 2) ** 2)
|
|
60
|
+
if (dd < d) (i = ii, d = dd, s = 'l')
|
|
61
|
+
dd = Math.sqrt((e.clientX - rect.right) ** 2 + (e.clientY - rect.y + rect.height / 2) ** 2)
|
|
62
|
+
if (dd < d) (i = ii, d = dd, s = 'r')
|
|
63
|
+
} else {
|
|
64
|
+
let dd = Math.sqrt((e.clientY - rect.y) ** 2 + (e.clientX - rect.x + rect.width / 2) ** 2)
|
|
65
|
+
if (dd < d) (i = ii, d = dd, s = 't')
|
|
66
|
+
dd = Math.sqrt((e.clientY - rect.bottom) ** 2 + (e.clientX - rect.x + rect.width / 2) ** 2)
|
|
67
|
+
if (dd < d) (i = ii, d = dd, s = 'b')
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const rect1 = children[i].getBoundingClientRect()
|
|
72
|
+
const x = s == 'l' || s == 'r', y = s == 't' || s == 'b'
|
|
73
|
+
const size = 3
|
|
74
|
+
state.style = {
|
|
75
|
+
width: `${y ? rect1.width : size}px`,
|
|
76
|
+
height: `${y ? size : rect1.height}px`,
|
|
77
|
+
translate: `${y ? rect1.x : (s == 'l' ? rect1.x : rect1.right) - size / 2}px ${x ? rect1.y : (s == 't' ? rect1.y : rect1.bottom) - size / 2}px`
|
|
78
|
+
}
|
|
79
|
+
state.rel = children[i]
|
|
80
|
+
state.type = s == 'l' || s == 't' ? 'before' : 'after'
|
|
81
|
+
} else {
|
|
82
|
+
const rect1 = over.getBoundingClientRect()
|
|
83
|
+
state.style = {
|
|
84
|
+
width: rect1.width,
|
|
85
|
+
height: rect1.height,
|
|
86
|
+
translate: `${rect1.x}px ${rect1.y}px`
|
|
87
|
+
}
|
|
88
|
+
state.rel = over
|
|
89
|
+
state.type = 'inner'
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
dragend() {
|
|
93
|
+
dragend()
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
function dragend() {
|
|
98
|
+
state.drag?.removeAttribute('draggable')
|
|
99
|
+
if (state.drag && state.rel) opt.dragend?.()
|
|
100
|
+
reconcile({})(state)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const state = createMutable({
|
|
104
|
+
style: void 0 as any,
|
|
105
|
+
drag: void 0 as any,
|
|
106
|
+
over: void 0 as any,
|
|
107
|
+
rel: void 0 as any,
|
|
108
|
+
type: ''
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
;(<>{state.style &&
|
|
112
|
+
<Portal mount={access(el)}>
|
|
113
|
+
<div {...opt.guideLine} style={state.style} />
|
|
114
|
+
</Portal>
|
|
115
|
+
}</>)
|
|
116
|
+
|
|
117
|
+
return state
|
|
118
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { createElementSize } from '@solid-primitives/resize-observer'
|
|
2
|
+
import { createScrollPosition } from '@solid-primitives/scroll'
|
|
3
|
+
import { keyBy, uniqBy } from 'es-toolkit'
|
|
4
|
+
import { createComputed, createMemo, createSignal, mergeProps, untrack } from 'solid-js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Fenwick Tree (Binary Indexed Tree)
|
|
8
|
+
* O(log n) point-update and prefix-sum query.
|
|
9
|
+
* Used to compute virtual item positions without rebuilding the full items array.
|
|
10
|
+
*/
|
|
11
|
+
class FenwickTree {
|
|
12
|
+
n: number
|
|
13
|
+
tree: Float64Array
|
|
14
|
+
constructor(n: number) {
|
|
15
|
+
this.n = n
|
|
16
|
+
this.tree = new Float64Array(n + 1)
|
|
17
|
+
}
|
|
18
|
+
/** O(log n) add delta to index i (0-based) */
|
|
19
|
+
add(i: number, delta: number) {
|
|
20
|
+
for (i += 1; i <= this.n; i += i & -i) this.tree[i] += delta
|
|
21
|
+
}
|
|
22
|
+
/** O(log n) prefix sum [0..i] inclusive (0-based) */
|
|
23
|
+
sum(i: number): number {
|
|
24
|
+
let s = 0
|
|
25
|
+
for (i += 1; i > 0; i -= i & -i) s += this.tree[i]
|
|
26
|
+
return s
|
|
27
|
+
}
|
|
28
|
+
/** Total sum of all elements */
|
|
29
|
+
total(): number {
|
|
30
|
+
return this.n > 0 ? this.sum(this.n - 1) : 0
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* O(log n) find the 0-based index of the element "containing" offset.
|
|
34
|
+
* Returns smallest i where prefix_sum(0..i) > target.
|
|
35
|
+
*/
|
|
36
|
+
findByOffset(target: number): number {
|
|
37
|
+
let i = 0
|
|
38
|
+
for (let pw = 1 << Math.floor(Math.log2(this.n || 1)); pw > 0; pw >>= 1) {
|
|
39
|
+
if (i + pw <= this.n && this.tree[i + pw] <= target) {
|
|
40
|
+
i += pw
|
|
41
|
+
target -= this.tree[i]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return Math.min(i, this.n - 1)
|
|
45
|
+
}
|
|
46
|
+
/** O(n) build from a sizes array (more efficient than n individual adds) */
|
|
47
|
+
static build(values: number[]): FenwickTree {
|
|
48
|
+
const n = values.length
|
|
49
|
+
const ft = new FenwickTree(n)
|
|
50
|
+
for (let i = 0; i < n; i++) {
|
|
51
|
+
ft.tree[i + 1] += values[i]
|
|
52
|
+
const j = i + 1 + ((i + 1) & -(i + 1))
|
|
53
|
+
if (j <= n) ft.tree[j] += ft.tree[i + 1]
|
|
54
|
+
}
|
|
55
|
+
return ft
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface VirtualizerOptions {
|
|
60
|
+
enable?: boolean
|
|
61
|
+
overscan?: number
|
|
62
|
+
batch?: number
|
|
63
|
+
getScrollElement: () => Element
|
|
64
|
+
horizontal?: boolean
|
|
65
|
+
count: number
|
|
66
|
+
estimateSize: (i: number) => number
|
|
67
|
+
extras?: (startIdx: number, endIdx: number) => number[]
|
|
68
|
+
indexAttribute?: string
|
|
69
|
+
rangeExtractor?: (range: any) => number[]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function useVirtualizer(opt: VirtualizerOptions) {
|
|
73
|
+
opt = mergeProps({ overscan: 0, batch: 0, enable: true }, opt)
|
|
74
|
+
const scrollSize = createElementSize(opt.getScrollElement)
|
|
75
|
+
const pos = createScrollPosition(opt.getScrollElement)
|
|
76
|
+
const scrollPos = createMemo(() => opt.horizontal ? pos.x : pos.y)
|
|
77
|
+
const viewSize = createMemo(() => opt.horizontal ? scrollSize.width : scrollSize.height)
|
|
78
|
+
|
|
79
|
+
// Plain (non-reactive) sizes array + Fenwick tree.
|
|
80
|
+
// Avoids O(n) full-rebuild on every resizeItem call.
|
|
81
|
+
const sizes: number[] = []
|
|
82
|
+
let tree = new FenwickTree(0)
|
|
83
|
+
|
|
84
|
+
// Version signal: bumped by resizeItem; triggers start/end/items recompute.
|
|
85
|
+
const [v, bumpV] = createSignal(undefined, { equals: false })
|
|
86
|
+
|
|
87
|
+
// Grow when count increases; shrink when it decreases.
|
|
88
|
+
createComputed(() => {
|
|
89
|
+
const { count } = opt
|
|
90
|
+
untrack(() => {
|
|
91
|
+
if (count === sizes.length) return
|
|
92
|
+
if (count > sizes.length) {
|
|
93
|
+
for (let i = sizes.length; i < count; i++) sizes.push(opt.estimateSize(i))
|
|
94
|
+
} else {
|
|
95
|
+
sizes.length = count
|
|
96
|
+
}
|
|
97
|
+
// O(n) rebuild of Fenwick tree preserving measured sizes
|
|
98
|
+
tree = FenwickTree.build(sizes.slice(0, count))
|
|
99
|
+
bumpV()
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
type Item = { start: number; end: number; index: number }
|
|
104
|
+
/** Compute item geometry directly from Fenwick tree — no array needed. */
|
|
105
|
+
const getItem = (i: number): Item => ({
|
|
106
|
+
index: i,
|
|
107
|
+
start: i > 0 ? tree.sum(i - 1) : 0,
|
|
108
|
+
end: tree.sum(i),
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
const startIdx = createMemo((prev: number) => {
|
|
112
|
+
v()
|
|
113
|
+
const { batch: batchSize, overscan = 0 } = opt
|
|
114
|
+
let i = tree.findByOffset(scrollPos()) - overscan
|
|
115
|
+
if (batchSize) {
|
|
116
|
+
if (i > prev) i = i <= prev + batchSize ? prev : (i > prev + batchSize * 2 ? i : prev + batchSize)
|
|
117
|
+
else i -= batchSize
|
|
118
|
+
}
|
|
119
|
+
return Math.max(i, 0)
|
|
120
|
+
}, 0)
|
|
121
|
+
|
|
122
|
+
const endIdx = createMemo((prev: number) => {
|
|
123
|
+
v()
|
|
124
|
+
const { batch: batchSize, overscan = 0 } = opt
|
|
125
|
+
let i = tree.findByOffset(scrollPos() + viewSize()) + overscan
|
|
126
|
+
if (batchSize) {
|
|
127
|
+
if (i < prev) i = i >= prev - batchSize ? prev : (i < prev - batchSize * 2 ? i : prev - batchSize)
|
|
128
|
+
else i += batchSize
|
|
129
|
+
}
|
|
130
|
+
return Math.min(i, opt.count - 1)
|
|
131
|
+
}, 0)
|
|
132
|
+
|
|
133
|
+
const items2 = createMemo(() => {
|
|
134
|
+
v()
|
|
135
|
+
if (!opt.enable) {
|
|
136
|
+
return Array.from({ length: opt.count }, (_, i) => getItem(i))
|
|
137
|
+
}
|
|
138
|
+
let arr: Item[] = []
|
|
139
|
+
for (let i = startIdx(); i <= endIdx(); i++) arr.push(getItem(i))
|
|
140
|
+
if (opt.extras) {
|
|
141
|
+
arr.push(...(opt.extras(startIdx(), endIdx())?.map(i => getItem(i)) || []))
|
|
142
|
+
arr = uniqBy(arr, e => e.index).sort((a, b) => a.index - b.index)
|
|
143
|
+
}
|
|
144
|
+
return arr
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
options: opt,
|
|
149
|
+
startIdx,
|
|
150
|
+
endIdx,
|
|
151
|
+
getTotalSize: () => (v(), tree.total()),
|
|
152
|
+
resizeItem: (i: number, newSize: number) => {
|
|
153
|
+
const old = sizes[i] ?? 0
|
|
154
|
+
if (old === newSize) return
|
|
155
|
+
// Scroll-position correction to prevent jitter when items above viewport resize
|
|
156
|
+
if (i < startIdx()) {
|
|
157
|
+
const el = opt.getScrollElement()
|
|
158
|
+
const scroll = opt.horizontal ? el.scrollLeft : el.scrollTop
|
|
159
|
+
if (scroll !== 0) {
|
|
160
|
+
opt.horizontal
|
|
161
|
+
? (el.scrollLeft += newSize - old)
|
|
162
|
+
: (el.scrollTop += newSize - old)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
tree.add(i, newSize - old)
|
|
166
|
+
sizes[i] = newSize
|
|
167
|
+
bumpV()
|
|
168
|
+
},
|
|
169
|
+
getVirtualItems: items2,
|
|
170
|
+
getVirtualItem: (() => {
|
|
171
|
+
const keyed = createMemo(() => {
|
|
172
|
+
v()
|
|
173
|
+
const map: Record<number, Item> = {}
|
|
174
|
+
for (const item of items2()) map[item.index] = item
|
|
175
|
+
return map
|
|
176
|
+
})
|
|
177
|
+
return (i: number) => keyed()[i]
|
|
178
|
+
})()
|
|
179
|
+
}
|
|
180
|
+
}
|