Skip to content

Commit

Permalink
feat(istanbul-widget): add float mode
Browse files Browse the repository at this point in the history
release
  • Loading branch information
hemengke1997 committed Apr 18, 2024
1 parent fa18ae2 commit b6f8ade
Show file tree
Hide file tree
Showing 15 changed files with 427 additions and 854 deletions.
6 changes: 6 additions & 0 deletions packages/istanbul-widget/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# istanbul-widget

## 1.1.0

### Minor Changes

- feat: float mode

## 1.0.3

### Patch Changes
Expand Down
1 change: 1 addition & 0 deletions packages/istanbul-widget/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ new IstanbulWidget({
y: 100,
},
report: {
auto: false,
onAction: async (coverage) => {
console.log('上报', coverage)
throw new Error('上报失败')
Expand Down
6 changes: 4 additions & 2 deletions packages/istanbul-widget/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "istanbul-widget",
"version": "1.0.3",
"version": "1.1.0",
"type": "module",
"main": "./dist/istanbul-widget.min.js",
"module": "./dist/istanbul-widget.esm.js",
Expand All @@ -16,14 +16,15 @@
"preview": "vite preview"
},
"dependencies": {
"@iconify-json/iconamoon": "^1.1.4",
"@iconify-json/vscode-icons": "^1.1.33",
"@minko-fe/lodash-pro": "^0.2.4",
"@minko-fe/react-hook": "^0.2.7",
"@neodrag/react": "^2.0.3",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-toast": "^1.1.5",
Expand All @@ -32,6 +33,7 @@
"destr": "^2.0.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.6",
"tailwind-merge": "^2.2.2",
"tailwind-variants": "^0.2.1",
"tailwindcss-animate": "^1.0.7"
Expand Down
4 changes: 2 additions & 2 deletions packages/istanbul-widget/src/components/ui/alert-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const AlertDialogContent = React.forwardRef<
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
"iw-fixed iw-left-[50%] iw-top-[50%] iw-z-50 iw-grid iw-w-[80%] iw-max-w-lg iw-translate-x-[-50%] iw-translate-y-[-50%] iw-space-4 iw-border iw-bg-background iw-p-6 iw-shadow-lg iw-duration-200 data-[state=open]:iw-animate-in data-[state=closed]:iw-animate-out data-[state=closed]:iw-fade-out-0 data-[state=open]:iw-fade-in-0 data-[state=closed]:iw-zoom-out-95 data-[state=open]:iw-zoom-in-95 data-[state=closed]:iw-slide-out-to-left-1/2 data-[state=closed]:iw-slide-out-to-top-[48%] data-[state=open]:iw-slide-in-from-left-1/2 data-[state=open]:iw-slide-in-from-top-[48%] iw-rounded-lg",
"iw-fixed iw-left-[50%] iw-top-[50%] iw-z-50 iw-grid iw-w-[80%] iw-max-w-lg iw-translate-x-[-50%] iw-translate-y-[-50%] iw-border iw-bg-background iw-p-6 iw-shadow-lg iw-duration-200 data-[state=open]:iw-animate-in data-[state=closed]:iw-animate-out data-[state=closed]:iw-fade-out-0 data-[state=open]:iw-fade-in-0 data-[state=closed]:iw-zoom-out-95 data-[state=open]:iw-zoom-in-95 data-[state=closed]:iw-slide-out-to-left-1/2 data-[state=closed]:iw-slide-out-to-top-[48%] data-[state=open]:iw-slide-in-from-left-1/2 data-[state=open]:iw-slide-in-from-top-[48%] iw-rounded-lg",
className
)}
{...props}
Expand All @@ -50,7 +50,7 @@ const AlertDialogHeader = ({
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"iw-flex iw-flex-col iw-space-y-2 iw-text-center sm:iw-text-left",
"iw-flex iw-flex-col iw-text-center sm:iw-text-left",
className
)}
{...props}
Expand Down
35 changes: 35 additions & 0 deletions packages/istanbul-widget/src/components/ui/popover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import { cn } from "@/components/utils"
import { ISTANBUL_WIDGET_ID } from '@/utils/tool'

const Popover = PopoverPrimitive.Root

const PopoverTrigger = PopoverPrimitive.Trigger

const PopoverAnchor = PopoverPrimitive.Anchor

const PopoverArrow = PopoverPrimitive.Arrow

const PopoverClose = PopoverPrimitive.Close

const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal container={document.querySelector(`#${ISTANBUL_WIDGET_ID}`) as HTMLElement}>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"iw-z-50 iw-w-fit iw-rounded-md iw-border iw-bg-popover iw-text-popover-foreground iw-shadow-md iw-outline-none data-[state=open]:iw-animate-in data-[state=closed]:iw-animate-out data-[state=closed]:iw-fade-out-0 data-[state=open]:iw-fade-in-0 data-[state=closed]:iw-zoom-out-95 data-[state=open]:iw-zoom-in-95 data-[side=bottom]:iw-slide-in-from-top-2 data-[side=left]:iw-slide-in-from-right-2 data-[side=right]:iw-slide-in-from-left-2 data-[side=top]:iw-slide-in-from-bottom-2",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
))
PopoverContent.displayName = PopoverPrimitive.Content.displayName

export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverArrow, PopoverClose }
116 changes: 69 additions & 47 deletions packages/istanbul-widget/src/core/IstanbulWidget.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { toNumber } from '@minko-fe/lodash-pro'
import { useDebounceFn, useSetState, useUpdateEffect } from '@minko-fe/react-hook'
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
import ReactDOM from 'react-dom/client'
import {
AlertDialog,
AlertDialogAction,
Expand All @@ -17,50 +16,24 @@ import { Button } from '@/components/ui/button'
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Popover, PopoverArrow, PopoverClose, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { Switch } from '@/components/ui/switch'
import { Toaster } from '@/components/ui/toaster'
import { useToast } from '@/components/ui/use-toast'
import { cn } from '@/components/utils'
import { $ } from '@/utils/query'
import { ISTANBUL_WIDGET_ID } from '@/utils/tool'
import Draggable from './components/Draggable'
import { type Config, type IstanbulWidgetOptions } from './options.interface'
import { type Config, type IstanbulWidgetOptions, type Position } from './options.interface'

export type CompInstance = {
destroy: () => void
update: (newProps: CoreProps) => void
}

export function render({ target, ...coreProps }: { target: HTMLElement } & CoreProps): CompInstance {
const container = document.createElement('div')
container.id = ISTANBUL_WIDGET_ID
target.appendChild(container)
const reactRoot = ReactDOM.createRoot(container)
reactRoot.render(<Core {...coreProps} />)

return {
destroy() {
reactRoot.unmount()
},
update(newProps: CoreProps) {
reactRoot.render(<Core {...coreProps} {...newProps} />)
},
}
}

type CoreProps = {
export type IstanbulWidgetProps = {
theme: IstanbulWidgetOptions['theme']
float: IstanbulWidgetOptions['float']
onAction: IstanbulWidgetOptions['report']['onAction']
beforeAction?: IstanbulWidgetOptions['report']['beforeAction']
afterAction?: IstanbulWidgetOptions['report']['afterAction']
position: {
x: number
y: number
}
defaultPosition: {
x: number
y: number
}
position: Position
defaultPosition: Position
show: boolean
min_internal: number
onConfigChanged: (c: Config) => void
Expand All @@ -70,7 +43,7 @@ type CoreProps = {
requireReporter: boolean
}

function Core(props: CoreProps) {
export default function IstanbulWidget(props: IstanbulWidgetProps) {
const {
position,
defaultPosition,
Expand All @@ -85,6 +58,7 @@ function Core(props: CoreProps) {
afterAction,
theme,
requireReporter,
float,
} = props

const { toast } = useToast()
Expand Down Expand Up @@ -218,6 +192,8 @@ function Core(props: CoreProps) {
}
}, [])

const dragging = useRef<boolean>(false)

return (
<>
<div
Expand All @@ -227,20 +203,64 @@ function Core(props: CoreProps) {
)}
>
<Dialog open={dialogOpen} onOpenChange={(open) => setDialogOpen(open)}>
<Draggable position={position} defaultPosition={defaultPosition}>
<div className='iw-flex iw-space-x-2'>
<Button size='sm' onClick={debouncedReport}>
上报
</Button>
<DialogTrigger asChild>
<Button size={'sm'} variant={'secondary'}>
设置
</Button>
</DialogTrigger>
<Popover>
<div>
<Draggable
position={position}
defaultPosition={defaultPosition}
dragOptions={{
onDrag() {
dragging.current = true
},
onDragEnd() {
const t = setTimeout(() => {
dragging.current = false
clearTimeout(t)
}, 60)
},
}}
float={float}
>
<PopoverTrigger
asChild
onClick={(e) => {
if (dragging.current) {
e.preventDefault()
return
}
}}
>
<div
className='iw-rounded-full iw-w-9 iw-h-9 iw-flex iw-justify-center iw-items-center iw-p-2'
style={{
backgroundColor: 'rgba(0, 0, 0, 0.3)',
}}
>
<div className='iw-icon-[vscode-icons--file-type-testjs] iw-w-full iw-h-full iw-cursor-pointer'></div>
</div>
</PopoverTrigger>
</Draggable>
</div>
</Draggable>
<PopoverContent sideOffset={2}>
<div className='iw-flex iw-items-center iw-space-x-2 iw-rounded-md iw-p-2 iw-text-xs iw-shadow'>
<PopoverClose asChild>
<Button size='sm' onClick={debouncedReport} data-state='closed'>
上报
</Button>
</PopoverClose>

<DialogContent>
<PopoverClose asChild>
<DialogTrigger asChild>
<Button size={'sm'} variant={'secondary'}>
设置
</Button>
</DialogTrigger>
</PopoverClose>
</div>
<PopoverArrow />
</PopoverContent>
</Popover>
<DialogContent onOpenAutoFocus={(e) => e.preventDefault()}>
<DialogHeader>
<DialogTitle>上报设置</DialogTitle>
</DialogHeader>
Expand Down Expand Up @@ -295,7 +315,9 @@ function Core(props: CoreProps) {
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>确认重置?</AlertDialogTitle>
<AlertDialogDescription>恢复默认设置</AlertDialogDescription>
<AlertDialogDescription>
<div className={'iw-my-3'}>恢复默认设置</div>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>取消</AlertDialogCancel>
Expand Down
56 changes: 39 additions & 17 deletions packages/istanbul-widget/src/core/components/Draggable.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,56 @@
import { useUpdateEffect } from '@minko-fe/react-hook'
import { useDraggable } from '@neodrag/react'
import { type DragOptions, useDraggable } from '@neodrag/react'
import { type PropsWithChildren, memo, useEffect, useRef, useState } from 'react'
import { cn } from '@/components/utils'
import { setStorage } from '@/utils/tool'
import { type IstanbulWidgetOptions, type Position } from '../options.interface'

type DraggableProps = PropsWithChildren<{
position: {
x: number
y: number
}
defaultPosition: {
x: number
y: number
}
position: Position
defaultPosition: Position
dragOptions: DragOptions
float: IstanbulWidgetOptions['float']
}>

function Draggable(props: DraggableProps) {
const { children, position: positionProp, defaultPosition } = props
const { children, position: positionProp, defaultPosition, dragOptions, float } = props

const draggableRef = useRef<HTMLDivElement>(null)
const handleRef = useRef<HTMLDivElement>(null)
const draggableRef = useRef<HTMLDivElement>(null)

const [dragging, setDragging] = useState(false)

const [position, setPosition] = useState({
x: positionProp.x || 0,
y: positionProp.y || 0,
})

useDraggable(draggableRef, {
...dragOptions,
position,
handle: handleRef,
onDrag: ({ offsetX, offsetY }) => {
onDragStart(data) {
setDragging(true)
dragOptions.onDragStart?.(data)
},
onDrag: (data) => {
const { offsetX, offsetY } = data
setPosition({ x: offsetX, y: offsetY })
dragOptions.onDrag?.(data)
},
onDragEnd(data) {
setDragging(false)

if (float) {
float.offset ??= 0
const { offsetX, offsetY } = data
const windowWidth = window.innerWidth
const w = handleRef.current!.getBoundingClientRect().width
const newPosition = offsetX + w / 2 > windowWidth / 2 ? windowWidth - w - float.offset : float.offset
setPosition({ x: newPosition, y: offsetY })
}

dragOptions.onDragEnd?.(data)
},
axis: 'both',
bounds: {
Expand Down Expand Up @@ -81,11 +103,11 @@ function Draggable(props: DraggableProps) {
}, [position])

return (
<div ref={draggableRef} className={'iw-w-fit iw-pointer-events-auto'}>
<div className='iw-flex iw-items-center iw-space-x-2 iw-rounded-md iw-bg-background iw-p-2 iw-text-xs iw-shadow'>
{children}
<div ref={handleRef} className='iw-icon-[iconamoon--move-fill] iw-cursor-move iw-text-lg iw-text-white'></div>
</div>
<div
ref={draggableRef}
className={cn('iw-w-fit iw-pointer-events-auto', !dragging ? 'iw-transition-transform' : '')}
>
<div ref={handleRef}>{children}</div>
</div>
)
}
Expand Down
Loading

0 comments on commit b6f8ade

Please sign in to comment.