I am new to this forum, not sure if this is how to post a question with some code, but I need some help please.
I have scene with some GridHelper objects to represent ‘pulleys’ which I need to drag but if I click on a child object of the GridHelper only the child is dragged, not the complete GridHelper.
// each pulley is a GridHelper with some child objects such as circle and ‘rim’ added for presentation
// I need to drag the GridHelper with its children in the dragcontrols
// but if the child object is clicked in the dragcontrols only the child is dragged
// if the GridHelper is clicked it move with the child objects - this is the required behavior
Sample project code based on Sean Bradley code follows
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Three.js TypeScript Tutorials by Sean Bradley : https://sbcode.net/threejs</title>
        <style>
            body {
                overflow: hidden;
                margin: 0px;
            }
        </style>
    </head>
    <body>
        <script type="module" src="bundle.js"></script>
    </body>
</html>
client.ts script
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
//import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { DragControls } from 'three/examples/jsm/controls/DragControls'
import Stats from 'three/examples/jsm/libs/stats.module'
const scene = new THREE.Scene()
scene.add(new THREE.AxesHelper(5))
const light = new THREE.SpotLight()
light.position.set(12.5, 12.5, 12.5)
light.castShadow = true
light.shadow.mapSize.width = 1024
light.shadow.mapSize.height = 1024
scene.add(light)
const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
)
camera.position.set(0, 0, 200)
const renderer = new THREE.WebGLRenderer()
//renderer.shadowMap.enabled = true
//renderer.outputEncoding = THREE.sRGBEncoding
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
scene.background = new THREE.Color(0xffffff);
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
controls.enableRotate = false
let cvDragObjects:  THREE.Object3D[] = [];
const dragcontrols = new DragControls(cvDragObjects, camera, renderer.domElement)
const pickableObjects: THREE.Mesh[] = []
let intersectedObject: THREE.Object3D | null
const originalMaterials: { [id: string]: THREE.Material | THREE.Material[] } =
    {}
const highlightedMaterial = new THREE.MeshBasicMaterial({
    wireframe: true,
    color: 0x00ff00
})
// create some data to draw
// each pulley is a GridHelper with some child objects such as circle and 'rim' added for presentation
// I need to drag the GridHelper with its children in the dragcontrols
// but if the child object is clicked in the dragcontrols only the child is dragged
// if the GridHelper is clicked it move with the child objects - this is the required behavior
let data = []
const datarow1 = { pType: "Bend", screenSize: 16, screenX: -50, screenZ: 50 }
data.push(datarow1)
const datarow2 = { pType: "Bend", screenSize: 12, screenX: 100, screenZ: 100 }
data.push(datarow2)
const datarow3 = { pType: "Bend", screenSize: 20, screenX: 50, screenZ: 100 }
data.push(datarow3)
const drive = { pType: "Drive", screenSize: 18, screenX: 150, screenZ: 70 }
data.push(drive)
const cv = new THREE.Object3D(); // main object in scene
// add the pulleys to the cv object
data.forEach(element => {
    const pull = drawPulley(element)
    cvDragObjects.push(pull)
    cv.add(pull)
});
scene.add(cv);
function drawPulley(dr1: any) {
    //var combinedgeometry = new THREE.Geometry();
    var rimmaterial = new THREE.LineBasicMaterial({ color: 0x000000 });
    var cenmaterial = new THREE.MeshBasicMaterial({ color: 0xa7a2a2 });
    var whitematerial = new THREE.MeshBasicMaterial({ color: 0xf7f7f7 });
    var redmaterial = new THREE.MeshBasicMaterial({ color: 0xf81010 });
    var blackmaterial = new THREE.MeshBasicMaterial({ color: 0x171616 });
    var bluematerial = new THREE.MeshBasicMaterial({ color: 0x1212fa });
    var gridhelpermaterial = new THREE.MeshBasicMaterial({ color: 0xa7a2a2, transparent: true, opacity: 0.01, visible: false });
    //var pulleyGrid = new THREE.PolarGridHelper(dr1.r, 16, 8, 4, cenmaterial.color, cenmaterial.color);
    var pulleyGrid = new THREE.GridHelper(dr1.screenSize * 0.5, 2, gridhelpermaterial.color, gridhelpermaterial.color);
    //pulleyGrid.rotateX(Math.PI / 2);
    if (dr1.pType === 'Drive') {
        //var r = dr1.r * .98;
        var r = dr1.screenSize * 0.5 * .98;
        var startAngle = 0;
        var angleIncr = Math.PI * 0.5;
        var geometry1 = new THREE.CircleGeometry(r, 32, startAngle, angleIncr);
        var circle1 = new THREE.Mesh(geometry1, redmaterial);
        circle1.userData = {
            isLineItem: false,
            isPullItem: true,
            no: -1,
            pType: 'driveface',
            isDragable: false
        };
        pulleyGrid.add(circle1);
        //pulleyGrid.attach(circle1);
        startAngle = startAngle + angleIncr;
        var geometry2 = new THREE.CircleGeometry(r, 32, startAngle, angleIncr);
        var circle2 = new THREE.Mesh(geometry2, whitematerial);
        circle2.userData = {
            isLineItem: false,
            isPullItem: true,
            no: -1,
            pType: 'driveface',
            isDragable: false
        };
        pulleyGrid.add(circle2);
        //pulleyGrid.attach(circle2);
        startAngle = startAngle + angleIncr;
        var geometry3 = new THREE.CircleGeometry(r, 32, startAngle, angleIncr);
        var circle3 = new THREE.Mesh(geometry3, redmaterial);
        circle3.userData = {
            isLineItem: false,
            isPullItem: true,
            no: -1,
            pType: 'driveface',
            isDragable: false
        };
        pulleyGrid.add(circle3);
        //pulleyGrid.attach(circle3);
        startAngle = startAngle + angleIncr;
        var geometry4 = new THREE.CircleGeometry(r, 32, startAngle, angleIncr);
        var circle4 = new THREE.Mesh(geometry4, whitematerial);
        circle4.userData = {
            isLineItem: false,
            isPullItem: true,
            no: -1,
            pType: 'driveface',
            isDragable: false
        };
        pulleyGrid.add(circle4);
        //pulleyGrid.attach(circle4);
    }
    else if (dr1.pType === 'Brake') {
        //var r = dr1.r * .98;
        var r = dr1.screenSize * 0.5 * .98;
        var startAngle = 0;
        var angleIncr = Math.PI * 0.5;
        var geometry1 = new THREE.CircleGeometry(r, 32, startAngle, angleIncr);
        var circle1 = new THREE.Mesh(geometry1, bluematerial);
        circle1.userData = {
            isLineItem: false,
            isPullItem: true,
            no: -1,
            pType: 'brakeface',
            isDragable: false
        };
        pulleyGrid.add(circle1);
        startAngle = startAngle + angleIncr;
        var geometry2 = new THREE.CircleGeometry(r, 32, startAngle, angleIncr);
        var circle2 = new THREE.Mesh(geometry2, whitematerial);
        circle2.userData = {
            isLineItem: false,
            isPullItem: true,
            no: -1,
            pType: 'brakeface',
            isDragable: false
        };
        pulleyGrid.add(circle2);
        startAngle = startAngle + angleIncr;
        var geometry3 = new THREE.CircleGeometry(r, 32, startAngle, angleIncr);
        var circle3 = new THREE.Mesh(geometry3, bluematerial);
        circle3.userData = {
            isLineItem: false,
            isPullItem: true,
            no: -1,
            pType: 'brakeface',
            isDragable: false
        };
        pulleyGrid.add(circle3);
        startAngle = startAngle + angleIncr;
        var geometry4 = new THREE.CircleGeometry(r, 32, startAngle, angleIncr);
        var circle4 = new THREE.Mesh(geometry4, whitematerial);
        circle4.userData = {
            isLineItem: false,
            isPullItem: true,
            no: -1,
            pType: 'brakeface',
            isDragable: false
        };
        pulleyGrid.add(circle4);
    }
    else if (dr1.pType === 'Takeup') {
        var r = dr1.screenSize * 0.5 * .98;
        var x = Math.cos(30 * Math.PI / 180) * r;
        var y = Math.sin(30 * Math.PI / 180) * r;
        var z = 0;
        //var material = new THREE.MeshStandardMaterial({ color: 0x000000 });
        var material = new THREE.MeshBasicMaterial({ color: 0x000000 });
        //create a triangular geometry
        //var geometry = new THREE.Geometry();
        const points = [];
        points.push(new THREE.Vector3(-x, -y, 0)); // use anticlockwise winding otherwise not shown
        points.push(new THREE.Vector3(x, -y, 0));
        points.push(new THREE.Vector3(0, r, 0));
        var geometry = new THREE.BufferGeometry().setFromPoints(points);
        //create a new face using vertices 0, 1, 2
        var normal = new THREE.Vector3(0, 0, 0); //optional
        var color = new THREE.Color(0x171616); //optional
        var materialIndex = 0; //optional
        //var vertexColors = [new THREE.Color(0x000000),
        //    new THREE.Color(0x000000),
        //    new THREE.Color(0x000000)];
        //var face = new THREE.Face3(0, 1, 2, normal, color, materialIndex);
        // code below not working Face3 removed
        // var face = new THREE.Face3(0, 1, 2);
        // //add the face to the geometry's faces array
        // geometry.faces.push(face);
        // //geometry.faces[0].vertexColors = vertexColors;
        // geometry.computeFaceNormals();
        geometry.computeVertexNormals();
        var facemesh = new THREE.Mesh(geometry, material);
        facemesh.userData = {
            isLineItem: false,
            isPullItem: true,
            no: -1,
            pType: 'takeupface',
            isDragable: false
        };
        pulleyGrid.add(facemesh);
    }
    else { // normal pulley
        var geometry1 = new THREE.CircleGeometry(dr1.screenSize * 0.5 - 1, 32);
        var circle1 = new THREE.Mesh(geometry1, cenmaterial);
        circle1.userData = {
            isLineItem: false,
            isPullItem: true,
            no: -1,
            pType: 'takeupface',
            isDragable: false
        };
        pulleyGrid.add(circle1);
        // var pullFace = this.drawPulleyFace(dr1.screenSize * 0.5, cenmaterial);
        // pulleyGrid.add(pullFace);
    }
    var rim = drawPulleyRim(dr1.screenSize * 0.5, rimmaterial);
    pulleyGrid.add(rim);
    //pulleyGrid.rotation.x = Math.PI / 2;
    pulleyGrid.userData = {
        isLineItem: false,
        isPullItem: true,
        no: dr1.no,
        pType: dr1.pType,
        isDragable: true
    };
    pulleyGrid.position.set(dr1.screenX, dr1.screenZ, 0/*dr1.y*/);
    pulleyGrid.name = dr1.pType + '-' + dr1.no;
    //console.log('pulleyGrid', pulleyGrid);
    return pulleyGrid;
};
function drawPulleyFace(radius: number, material: THREE.MeshBasicMaterial) {
    //var geometry1 = new THREE.CircleGeometry(dr1.r, 32);
    var geometry1 = new THREE.CircleGeometry(radius - 1, 32);
    //var circle1 = new THREE.Mesh(geometry1, cenmaterial, 0, Math.PI * 2);
    var circle1 = new THREE.Mesh(geometry1, material);
    //circle1.name('pulleyFill') // no name property
    //pulleyGrid.add(circle1);
    //circle1.removeEventListener;
    circle1.userData = {
        isLineItem: false,
        isPullItem: true,
        no: -1,
        pType: 'pulleyface',
        isDragable: false
    };
    return circle1;
}
function drawPulleyRim(radius: number, material: THREE.LineBasicMaterial) {
    var segmentCount = 32;
    //geometry = new THREE.Geometry();
    //geometry = new THREE.BufferGeometry();
    //material = new THREE.LineBasicMaterial({ color: 0x555 });
    const vertices = [];
    for (var i = 0; i <= segmentCount; i++) {
        var theta = (i / segmentCount) * Math.PI * 2;
        // geometry.vertices.push(
        //   new THREE.Vector3(
        //     Math.cos(theta) * radius,
        //     Math.sin(theta) * radius,
        //     0));
        // see https://sbcode.net/threejs/geometry-to-buffergeometry/ on jow to use the new BufferGeometry
        vertices.push(
            new THREE.Vector3(
                Math.cos(theta) * radius,
                Math.sin(theta) * radius,
                0));
    }
    // draw a second rim for extra thickness
    for (var i = 0; i <= segmentCount; i++) {
        var theta = (i / segmentCount) * Math.PI * 2;
        vertices.push(
            new THREE.Vector3(
                Math.cos(theta) * (radius - 0.5),
                Math.sin(theta) * (radius - 0.5),
                0));
    }
    //geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
    let geometry = new THREE.BufferGeometry().setFromPoints(vertices);
    let rim = new THREE.Line(geometry, material);
    rim.userData = {
        isLineItem: false,
        isPullItem: true,
        no: -1,
        pType: 'pulleyrim',
        isDragable: false
    };
    return rim;
};
window.addEventListener('resize', onWindowResize, false)
function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()
    renderer.setSize(window.innerWidth, window.innerHeight)
    render()
}
const raycaster = new THREE.Raycaster()
let intersects: THREE.Intersection[]
// document.addEventListener('mousemove', onDocumentMouseMove, false)
// function onDocumentMouseMove(event: MouseEvent) {
//     raycaster.setFromCamera(
//         {
//             x: (event.clientX / renderer.domElement.clientWidth) * 2 - 1,
//             y: -(event.clientY / renderer.domElement.clientHeight) * 2 + 1
//         },
//         camera
//     )
//     intersects = raycaster.intersectObjects(pickableObjects, false)
//     if (intersects.length > 0) {
//         intersectedObject = intersects[0].object
//     } else {
//         intersectedObject = null
//     }
//     pickableObjects.forEach((o: THREE.Mesh, i) => {
//         if (intersectedObject && intersectedObject.name === o.name) {
//             pickableObjects[i].material = highlightedMaterial
//         } else {
//             pickableObjects[i].material = originalMaterials[o.name]
//         }
//     })
// }
const stats = Stats()
document.body.appendChild(stats.dom)
function animate() {
    requestAnimationFrame(animate)
    controls.update()
    render()
    stats.update()
}
function render() {
    renderer.render(scene, camera)
}
animate()