File size: 6,044 Bytes
e1753d8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import { 
    track, 
    useEditor, 
    useValue, 
    TldrawUiButton, 
    TldrawUiButtonIcon,
    atom, 
    Box,
    react
} from 'tldraw'
import { useEffect } from 'react'
import { LockIcon } from './Icons'

export const getSelectLockedSignal = (editor: any) => {
    if (!editor._selectLockedSignal) {
        editor._selectLockedSignal = atom('selectLocked', !!editor._selectLocked)
    }
    return editor._selectLockedSignal
}

export const SelectOptionsPanel = track(() => {
    const editor = useEditor()
    const isSelect = useValue('isSelect', () => editor.getCurrentToolId() === 'select', [editor])
    const isSelectLocked = useValue('isSelectLocked', () => getSelectLockedSignal(editor).get(), [editor])

    if (!isSelect) return null

    return (
        <div style={{
            position: 'absolute',
            bottom: 84,
            left: 12,
            zIndex: 1000,
            display: 'flex',
            flexDirection: 'column',
            gap: '8px',
            background: 'var(--tl-color-panel)',
            padding: '8px',
            borderRadius: '8px',
            boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
            border: '1px solid var(--tl-color-divider)',
            minWidth: '150px'
        }}>
            <div style={{ fontSize: '12px', fontWeight: 'bold', marginBottom: '4px', color: 'var(--tl-color-text)', textAlign: 'center' }}>Select Options</div>
            <TldrawUiButton
                type="low"
                onClick={() => {
                    const signal = getSelectLockedSignal(editor)
                    const next = !signal.get()
                    signal.set(next)
                    ;(editor as any)._selectLocked = next
                    editor.emit('select-locked-changed' as any)
                }}
                style={{
                    justifyContent: 'flex-start',
                    padding: '4px 8px',
                    height: '32px',
                    background: isSelectLocked ? 'var(--tl-color-selected)' : 'transparent',
                    color: isSelectLocked ? 'white' : 'var(--tl-color-text)',
                }}
            >
                <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
                    <div style={{ width: '16px', display: 'flex', justifyContent: 'center' }}>
                        {isSelectLocked ? <LockIcon size={14} /> : <div style={{ width: 14, height: 14, border: '1px solid currentColor', borderRadius: '2px' }} />}
                    </div>
                    <span>Select Locked</span>
                </div>
            </TldrawUiButton>
        </div>
    )
})

export const SelectLockedToggle = track(() => {
    const editor = useEditor()
    const isSelectLocked = useValue('isSelectLocked', () => getSelectLockedSignal(editor).get(), [editor])

    return (
        <TldrawUiButton
            type="icon"
            title="Select Locked Objects"
            isActive={isSelectLocked}
            onClick={() => {
                const signal = getSelectLockedSignal(editor)
                const next = !signal.get()
                signal.set(next)
                ;(editor as any)._selectLocked = next
                editor.emit('select-locked-changed' as any)
            }}
        >
            <TldrawUiButtonIcon icon={isSelectLocked ? 'lock' : 'unlock'} />
        </TldrawUiButton>
    )
})

export function SelectLockedLogic() {
    const editor = useEditor()
    
    // Handle click-to-select locked shapes
    useEffect(() => {
        const handleEvent = (event: any) => {
            if (event.name !== 'pointer_down') return
            
            const isSelectLocked = getSelectLockedSignal(editor).get()
            if (!isSelectLocked) return
            if (editor.getCurrentToolId() !== 'select') return
            
            const { x, y } = editor.inputs.currentPagePoint
            const pagePoint = { x, y }
            
            const shapes = editor.getCurrentPageShapesSorted()
            let target = null
            for (let i = shapes.length - 1; i >= 0; i--) {
                const shape = shapes[i]
                if (shape.isLocked && editor.isPointInShape(shape, pagePoint, { hitInside: true, margin: 0 })) {
                    target = shape
                    break
                }
            }
            
            if (target) {
                setTimeout(() => {
                    editor.setSelectedShapes([target!.id])
                }, 50)
            }
        }
        
        editor.on('event', handleEvent)
        return () => {
            editor.off('event', handleEvent)
        }
    }, [editor])

    // Handle drag-to-select (brush) locked shapes - OPTIMIZED
    useEffect(() => {
        return react('check-brush-locked', () => {
            const isSelectLocked = getSelectLockedSignal(editor).get()
            if (!isSelectLocked) return
            if (editor.getCurrentToolId() !== 'select') return

            const brushModel = editor.getInstanceState().brush
            if (!brushModel) return

            const brush = Box.From(brushModel)
            const shapes = editor.getCurrentPageShapesSorted()
            const lockedInBrush = shapes.filter(shape => {
                if (!shape.isLocked) return false
                const bounds = editor.getShapePageBounds(shape)
                if (!bounds) return false
                return brush.collides(bounds)
            })

            if (lockedInBrush.length > 0) {
                const currentSelected = new Set(editor.getSelectedShapeIds())
                let changed = false
                for (const shape of lockedInBrush) {
                    if (!currentSelected.has(shape.id)) {
                        currentSelected.add(shape.id)
                        changed = true
                    }
                }
                if (changed) {
                    editor.setSelectedShapes(Array.from(currentSelected))
                }
            }
        })
    }, [editor])
    
    return null
}