wwf
2025-05-20 938c3e5a587ce950a94964ea509b9e7f8834dfae
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { useState } from 'react'
import type { FC, ReactNode } from 'react'
import { FloatingFocusManager, type OffsetOptions, autoUpdate, flip, offset, shift, useDismiss, useFloating, useHover, useInteractions, useRole } from '@floating-ui/react'
import { RiDeleteBinLine } from '@remixicon/react'
// @ts-expect-error no types available
import lineClamp from 'line-clamp'
import type { SliceProps } from './type'
import { SliceContainer, SliceContent, SliceDivider, SliceLabel } from './shared'
import classNames from '@/utils/classnames'
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
 
type EditSliceProps = SliceProps<{
  label: ReactNode
  onDelete: () => void
  labelClassName?: string
  labelInnerClassName?: string
  contentClassName?: string
  showDivider?: boolean
  offsetOptions?: OffsetOptions
}>
 
export const EditSlice: FC<EditSliceProps> = (props) => {
  const {
    label,
    className,
    text,
    onDelete,
    labelClassName,
    labelInnerClassName,
    contentClassName,
    showDivider = true,
    offsetOptions,
    ...rest
  } = props
  const [delBtnShow, setDelBtnShow] = useState(false)
  const [isDelBtnHover, setDelBtnHover] = useState(false)
 
  const { refs, floatingStyles, context } = useFloating({
    open: delBtnShow,
    onOpenChange: setDelBtnShow,
    placement: 'right-start',
    whileElementsMounted: autoUpdate,
    middleware: [
      flip(),
      shift(),
      offset(offsetOptions),
    ],
  })
  const hover = useHover(context, {})
  const dismiss = useDismiss(context)
  const role = useRole(context)
  const { getReferenceProps, getFloatingProps } = useInteractions([hover, dismiss, role])
 
  const isDestructive = delBtnShow && isDelBtnHover
 
  return (
    <>
      <SliceContainer {...rest}
        className={classNames('block mr-0', className)}
        ref={(ref) => {
          refs.setReference(ref)
          if (ref)
            lineClamp(ref, 4)
        }}
        {...getReferenceProps()}
      >
        <SliceLabel
          className={classNames(
            isDestructive && '!bg-state-destructive-solid !text-text-primary-on-surface',
            labelClassName,
          )}
          labelInnerClassName={labelInnerClassName}
        >
          {label}
        </SliceLabel>
        <SliceContent
          className={classNames(
            isDestructive && '!bg-state-destructive-hover-alt',
            contentClassName,
          )}
        >
          {text}
        </SliceContent>
        {showDivider && <SliceDivider
          className={classNames(
            isDestructive && '!bg-state-destructive-hover-alt',
          )}
        />}
        {delBtnShow && <FloatingFocusManager
          context={context}
        >
          <span
            ref={refs.setFloating}
            style={floatingStyles}
            {...getFloatingProps()}
            className='inline-flex items-center justify-center rounded-lg bg-components-actionbar-bg p-1 shadow'
            onMouseEnter={() => setDelBtnHover(true)}
            onMouseLeave={() => setDelBtnHover(false)}
          >
            <ActionButton
              onClick={(e) => {
                e.stopPropagation()
                onDelete()
                setDelBtnShow(false)
              }}
              state={ActionButtonState.Destructive}
            >
              <RiDeleteBinLine className='h-4 w-4' />
            </ActionButton>
          </span>
        </FloatingFocusManager>}
      </SliceContainer>
    </>
  )
}