Skip to content

Commit 7838076

Browse files
committed
update
1 parent 2fb2e7a commit 7838076

File tree

24 files changed

+363
-106
lines changed

24 files changed

+363
-106
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
body {
2+
position: relative;
3+
overflow: hidden;
4+
margin: 0;
5+
height:100;
6+
height: 100dvh;
7+
height: --webkit-fill-available;
8+
font-family: "Pretendard Variable", sans-serif;
9+
}
10+
button, a {
11+
font-family: inherit;
12+
-webkit-tap-highlight-color: transparent;
13+
}
14+
15+
button:focus-visible, a:focus-visible {
16+
outline: 2px auto black;
17+
}

apps/bplan-client/src/components/button/HButton.tsx

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
import {now} from '@winter-love/lodash'
12
import {JSX, splitProps} from 'solid-js'
23

34
export interface HButtonProps
4-
extends Omit<JSX.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'onTouchEnd'> {
5+
extends Omit<
6+
JSX.ButtonHTMLAttributes<HTMLButtonElement>,
7+
'onClick' | 'onTouchEnd' | 'onDblClick'
8+
> {
59
onClick?: JSX.EventHandler<HTMLButtonElement, MouseEvent | TouchEvent>
10+
onDoubleClick?: JSX.EventHandler<HTMLButtonElement, MouseEvent | TouchEvent>
611
onTouchEnd?: JSX.EventHandler<HTMLButtonElement, TouchEvent>
712
}
813

@@ -25,7 +30,13 @@ export interface HButtonProps
2530
* @prop {JSX.EventHandler<HTMLButtonElement, TouchEvent>} [onTouchEnd] - Event handler for the `touchend` event.
2631
*/
2732
export const HButton = (props: HButtonProps) => {
28-
const [innerProps, restProps] = splitProps(props, ['onClick', 'onTouchEnd'])
33+
const doubleClickGap = 200
34+
let clickTime = 0
35+
const [innerProps, restProps] = splitProps(props, [
36+
'onClick',
37+
'onTouchEnd',
38+
'onDoubleClick',
39+
])
2940
/**
3041
* Handles the `click` event for the button component and forwards it to the parent component.
3142
*
@@ -40,13 +51,32 @@ export const HButton = (props: HButtonProps) => {
4051
innerProps.onClick?.(event)
4152
}
4253

54+
const handleDoubleClick: HButtonProps['onDoubleClick'] = (event) => {
55+
innerProps.onDoubleClick?.(event)
56+
}
57+
4358
const handleTouchEnd: HButtonProps['onTouchEnd'] = (event) => {
44-
innerProps.onClick?.(event)
4559
innerProps.onTouchEnd?.(event)
60+
innerProps.onClick?.(event)
61+
62+
const newClickTime = now()
63+
64+
if (newClickTime - clickTime < doubleClickGap) {
65+
handleDoubleClick(event)
66+
67+
return
68+
}
69+
70+
clickTime = newClickTime
4671
}
4772

4873
return (
49-
<button {...restProps} on:click={handleClick} onTouchEnd={handleTouchEnd}>
74+
<button
75+
{...restProps}
76+
onClick={handleClick}
77+
onDblClick={handleDoubleClick}
78+
onTouchEnd={handleTouchEnd}
79+
>
5080
{props.children}
5181
</button>
5282
)

apps/bplan-client/src/components/midi-player/SMidiFileInput.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {createUniqueId, JSX, onCleanup, splitProps} from 'solid-js'
1+
import {createSignal, createUniqueId, JSX, onCleanup, splitProps} from 'solid-js'
22
import {cx} from 'class-variance-authority'
33
import type {Midi} from '@tonejs/midi'
44
import {MusicInfo} from 'src/components/midi-player/SFileItem'
@@ -23,6 +23,7 @@ const rootStyle = cx(
2323
export const SMidiFileInput = (props: HMidiFileInputProps) => {
2424
const id = createUniqueId()
2525
const [innerProps, restProps] = splitProps(props, ['class', 'onAdd', 'onTouchEnd'])
26+
const [inputElement, setInputElement] = createSignal<HTMLInputElement | null>(null)
2627
let isCleanup = false
2728
const handleInputFiles = async (files: FileList | null) => {
2829
if (!files || files.length === 0) {
@@ -100,13 +101,26 @@ export const SMidiFileInput = (props: HMidiFileInputProps) => {
100101
innerProps.onAdd?.(samples)
101102
}
102103

104+
const handleKeyDown = (event: KeyboardEvent) => {
105+
const element = inputElement()
106+
107+
if (event.key === 'Enter' && element) {
108+
element.click()
109+
}
110+
}
111+
103112
onCleanup(() => {
104113
isCleanup = true
105114
})
106115

107116
return (
108117
<div class={cx(rootStyle, innerProps.class)}>
109-
<label class="inline-flex text-inherit" for={id}>
118+
<label
119+
class="inline-flex text-inherit"
120+
for={id}
121+
tabIndex="0"
122+
onKeyDown={handleKeyDown}
123+
>
110124
<span class="text-nowrap md:text-6 text-4 md:pt-.5 pt-1.5">Click or Drop </span>
111125
<span class="block i-tabler:file-plus text-6 px-1 pt-2" />
112126
<span class="text-nowrap md:inline hidden md:text-6 text-4 md:pt-.5">
@@ -117,6 +131,8 @@ export const SMidiFileInput = (props: HMidiFileInputProps) => {
117131
{props.children}
118132
<input
119133
{...restProps}
134+
ref={setInputElement}
135+
tabIndex="-1"
120136
title=""
121137
type="file"
122138
multiple

apps/bplan-client/src/components/midi-player/SSeeker.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export const SSeeker = (props: SSeekerProps) => {
6464
return (
6565
<button
6666
{...restProps}
67+
tabIndex="-1"
6768
class={cx(props.class ?? 'relative', 'cursor-pointer')}
6869
onClick={handleClick}
6970
onTouchStart={handleTouchStart}

apps/bplan-client/src/entry-server.tsx

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,7 @@ export default createHandler(() => (
2525
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
2626
<link rel="mask-icon" href="/favicon.svg" color="#00aba9" />
2727
<link rel="manifest" href="/manifest.json" />
28-
<style type="text/css">
29-
{`
30-
body {
31-
position: relative;
32-
overflow: hidden;
33-
margin: 0;
34-
height:100;
35-
height: 100dvh;
36-
height: --webkit-fill-available;
37-
font-family: "Pretendard Variable", sans-serif;
38-
}
39-
button {
40-
font-family: inherit;
41-
}
42-
`}
43-
</style>
28+
<link rel="stylesheet" href="/normalize.css" />
4429
{assets}
4530
</head>
4631
<body>

apps/bplan-client/src/routes/(main-layout).tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {RouteSectionProps, useSearchParams} from '@solidjs/router'
22
import {useStorage} from '@winter-love/solid-use'
3-
import {createEffect, createResource} from 'solid-js'
3+
import {createMemo, createResource} from 'solid-js'
44
import {
55
MusicInfo,
66
SettingContext,
@@ -45,17 +45,20 @@ export default function MainLayout(props: RouteSectionProps) {
4545
})
4646
const [searchParams] = useSearchParams<{preset?: string}>()
4747
const [preset] = createResource(() => getPreset(searchParams.preset))
48+
4849
const [settingData, setSettingData] = useCookie<SettingData>('coong__piano-setting', {
4950
keepPlayList: true,
5051
pianoSize: 100,
5152
showKeyName: false,
5253
})
53-
const [musics, setMusics, updateActive] = useStorage<MusicInfo[]>(
54+
const isActiveStore = createMemo(() => Boolean(settingData().keepPlayList))
55+
const [musics, setMusics] = useStorage<MusicInfo[]>(
5456
'local',
5557
'coong:piano-musics-default',
56-
[],
5758
{
59+
active: isActiveStore,
5860
enforceValue: preset()?.musics,
61+
initValue: [],
5962
mounted: true,
6063
},
6164
)
@@ -66,10 +69,6 @@ export default function MainLayout(props: RouteSectionProps) {
6669
setMusics(musics)
6770
}
6871

69-
createEffect(() => {
70-
updateActive(Boolean(settingData().keepPlayList))
71-
})
72-
7372
return (
7473
<SettingContext.Provider value={settingData}>
7574
<SplendidGrandPianoContext.Provider
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {describe, expect, it, vi} from 'vitest'
2+
import {createTrigger} from '../'
3+
4+
describe('trigger', () => {
5+
it('should be created', () => {
6+
const trigger = createTrigger()
7+
expect(trigger).toBeDefined()
8+
})
9+
it('should set target and run it', () => {
10+
const trigger = createTrigger()
11+
const callback = vi.fn()
12+
13+
trigger.target = callback
14+
trigger.run()
15+
expect(callback).toHaveBeenCalled()
16+
})
17+
})

packages/solid-use/src/animation-loop/__tests__/index.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ import {createTrigger} from '@winter-love/solid-test'
88

99
describe('useAnimationLoop', () => {
1010
const animationTrigger = createTrigger()
11+
const cancelFlag = 1
12+
1113
const requestAnimationFrame: any = vi.fn((callback) => {
1214
animationTrigger.target = callback
15+
16+
return cancelFlag
1317
})
1418
const cancelAnimationFrame = vi.fn()
1519

@@ -20,6 +24,8 @@ describe('useAnimationLoop', () => {
2024
afterEach(() => {
2125
vi.spyOn(window, 'requestAnimationFrame').mockRestore()
2226
vi.spyOn(window, 'cancelAnimationFrame').mockRestore()
27+
requestAnimationFrame.mockClear()
28+
cancelAnimationFrame.mockClear()
2329
})
2430
it('should call callback many in animation frame', () => {
2531
const callback = vi.fn()
@@ -38,4 +44,32 @@ describe('useAnimationLoop', () => {
3844
expect(callback).toHaveBeenCalledTimes(1)
3945
expect(animationTrigger.changed).toBe(2)
4046
})
47+
it('should cancel animation frame with dispose', () => {
48+
const callback = vi.fn()
49+
const {animationLoop, dispose} = createRoot((dispose) => {
50+
const animationLoop = useAnimationLoop(callback)
51+
52+
return {animationLoop, dispose}
53+
})
54+
55+
animationLoop.start()
56+
expect(requestAnimationFrame).toHaveBeenCalledTimes(1)
57+
dispose()
58+
expect(cancelAnimationFrame).toHaveBeenNthCalledWith(1, cancelFlag)
59+
expect(callback).not.toHaveBeenCalled()
60+
})
61+
it('should cancel animation frame with stop', () => {
62+
const callback = vi.fn()
63+
const {animationLoop} = createRoot((dispose) => {
64+
const animationLoop = useAnimationLoop(callback)
65+
66+
return {animationLoop, dispose}
67+
})
68+
69+
animationLoop.start()
70+
expect(requestAnimationFrame).toHaveBeenCalledTimes(1)
71+
animationLoop.stop()
72+
expect(cancelAnimationFrame).toHaveBeenNthCalledWith(1, cancelFlag)
73+
expect(callback).not.toHaveBeenCalled()
74+
})
4175
})
Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
11
import {useDebounce} from '../'
2-
import {describe, expect, it, vi} from 'vitest'
3-
import {useFakeTimers} from 'sinon'
2+
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
3+
import {SinonFakeTimers, useFakeTimers} from 'sinon'
4+
import {createRoot} from 'solid-js'
45

56
describe('useDebounce', () => {
7+
let timer: SinonFakeTimers
8+
9+
beforeEach(() => {
10+
timer = useFakeTimers()
11+
})
12+
afterEach(() => {
13+
timer.restore()
14+
})
615
it('should debounce calling the callback function', () => {
7-
const timer = useFakeTimers()
816
const options = {leading: true}
917
const args = ['hello']
1018
const callback = vi.fn()
11-
const debounce = useDebounce(callback, 100, options)
19+
const {debounce, dispose} = createRoot((dispose) => {
20+
const debounce = useDebounce(callback, 100, options)
21+
22+
return {debounce, dispose}
23+
})
1224

1325
debounce.execute(...args)
1426
expect(callback).toHaveBeenCalledTimes(1)
@@ -20,6 +32,58 @@ describe('useDebounce', () => {
2032
expect(callback).toHaveBeenCalledTimes(1)
2133
timer.tick(100)
2234
expect(callback).toHaveBeenCalledTimes(2)
23-
timer.restore()
35+
dispose()
36+
})
37+
it('should cancel debounce with dispose', () => {
38+
const options = {leading: true}
39+
const args = ['hello']
40+
const callback = vi.fn()
41+
const {debounce, dispose} = createRoot((dispose) => {
42+
const debounce = useDebounce(callback, 100, options)
43+
44+
return {debounce, dispose}
45+
})
46+
47+
debounce.execute(...args)
48+
timer.tick(50)
49+
debounce.execute(...args)
50+
dispose()
51+
timer.tick(100)
52+
expect(callback).toHaveBeenCalledTimes(1)
53+
})
54+
it('should cancel debounce', () => {
55+
const options = {leading: true}
56+
const args = ['hello']
57+
const callback = vi.fn()
58+
const {debounce} = createRoot((dispose) => {
59+
const debounce = useDebounce(callback, 100, options)
60+
61+
return {debounce, dispose}
62+
})
63+
64+
debounce.execute(...args)
65+
timer.tick(50)
66+
debounce.execute(...args)
67+
debounce.cancel()
68+
timer.tick(100)
69+
expect(callback).toHaveBeenCalledTimes(1)
70+
})
71+
it('should flush debounce', () => {
72+
const options = {leading: true}
73+
const args = ['hello']
74+
const callback = vi.fn()
75+
const {debounce} = createRoot((dispose) => {
76+
const debounce = useDebounce(callback, 100, options)
77+
78+
return {debounce, dispose}
79+
})
80+
81+
debounce.execute(...args)
82+
timer.tick(50)
83+
debounce.execute(...args)
84+
debounce.flush()
85+
expect(callback).toHaveBeenCalledTimes(2)
86+
timer.tick(100)
87+
expect(callback).toHaveBeenCalledTimes(2)
2488
})
2589
})

packages/solid-use/src/drag/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {createSignal} from 'solid-js'
2-
import {MayBeAccessor} from 'src/types'
2+
import {MaybeAccessor} from 'src/types'
33
import {useEvent} from 'src/event'
44
import {getWindow} from '@winter-love/utils'
55
import {toggleValue} from 'src/toggle-value'
@@ -27,7 +27,7 @@ interface DragPayload {
2727
}
2828

2929
export const useDrag = (
30-
handleElement: MayBeAccessor<HTMLElement | null>,
30+
handleElement: MaybeAccessor<HTMLElement | null>,
3131
callback: (type: DragType, payload: DragPayload) => void,
3232
) => {
3333
const [startPoints, setStartPoints] = createSignal<StartPoints>({

0 commit comments

Comments
 (0)