collect values from multiple users
This commit is contained in:
parent
d34955e5dd
commit
d6524330cc
13
src/App.tsx
13
src/App.tsx
|
|
@ -5,6 +5,7 @@ import { AgentButton } from "./components/AgentButton";
|
|||
import { SocialShapeUtil } from "./SocialShapeUtil";
|
||||
import { SocialShapeTool } from "./SocialShapeTool";
|
||||
import { CustomToolbar, overrides } from "./ui";
|
||||
import { getDocumentMeta, getUserId, getUsersInRoom, setDocumentMeta } from "./storeUtils";
|
||||
|
||||
const shapeUtils = [SocialShapeUtil];
|
||||
const tools = [SocialShapeTool];
|
||||
|
|
@ -34,6 +35,18 @@ export default function Canvas() {
|
|||
onMount={(editor) => {
|
||||
//@ts-ignore
|
||||
editor.getStateDescendant('select.idle').handleDoubleClickOnCanvas = () => void null;
|
||||
console.log(editor.store.allRecords())
|
||||
const userId = getUserId(editor)
|
||||
setDocumentMeta(editor, {
|
||||
[userId]: 123
|
||||
})
|
||||
// console.log(getDocumentMeta(editor))
|
||||
// removeDocumentMeta(editor, 'test')
|
||||
setTimeout(() => {
|
||||
console.log(getDocumentMeta(editor))
|
||||
console.log(getUsersInRoom(editor))
|
||||
}, 2000);
|
||||
|
||||
}}
|
||||
components={{
|
||||
SharePanel: AgentButton,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
resizeBox,
|
||||
toDomPrecision,
|
||||
} from 'tldraw'
|
||||
import { getUserId } from './storeUtils'
|
||||
|
||||
export type ValueType = "scalar" | "boolean" | null
|
||||
|
||||
|
|
@ -19,6 +20,7 @@ export type ISocialShape = TLBaseShape<
|
|||
text: string
|
||||
selector: string
|
||||
valueType: ValueType
|
||||
values: Record<string, any>
|
||||
}
|
||||
>
|
||||
|
||||
|
|
@ -27,7 +29,7 @@ export class SocialShapeUtil extends BaseBoxShapeUtil<ISocialShape> {
|
|||
override canBind = () => false
|
||||
override canEdit = () => false
|
||||
override getDefaultProps(): ISocialShape['props'] {
|
||||
return { w: 160 * 2, h: 90 * 2, text: '', selector: '', valueType: null }
|
||||
return { w: 160 * 2, h: 90 * 2, text: '', selector: '', valueType: null, values: {} }
|
||||
}
|
||||
override onResize: TLOnResizeHandler<ISocialShape> = (shape, info) => {
|
||||
return resizeBox(shape, info)
|
||||
|
|
@ -39,6 +41,7 @@ export class SocialShapeUtil extends BaseBoxShapeUtil<ISocialShape> {
|
|||
isFilled: true,
|
||||
})
|
||||
}
|
||||
|
||||
indicator(shape: ISocialShape) {
|
||||
const bounds = this.editor.getShapeGeometry(shape).bounds
|
||||
return (
|
||||
|
|
@ -50,16 +53,22 @@ export class SocialShapeUtil extends BaseBoxShapeUtil<ISocialShape> {
|
|||
)
|
||||
}
|
||||
|
||||
override component(machine: ISocialShape) {
|
||||
override component(shape: ISocialShape) {
|
||||
const currentUser = getUserId(this.editor)
|
||||
|
||||
const handleOnChange = (newValue: any) => {
|
||||
this.updateProps(shape, { values: { ...shape.props.values, [currentUser]: newValue } })
|
||||
console.log(shape.props.values)
|
||||
}
|
||||
|
||||
return (
|
||||
<HTMLContainer style={{ padding: 4, borderRadius: 4, border: '1px solid #ccc', pointerEvents: 'all' }}>
|
||||
<textarea style={{ width: '100%', height: '100%', border: 'none', outline: 'none', resize: 'none' }} value={machine.props.text} onChange={(e) => this.updateProps(machine, { text: e.target.value })} />
|
||||
<HTMLContainer style={{ padding: 4, borderRadius: 4, border: '1px solid #ccc' }} onPointerDown={(e) => e.stopPropagation()}>
|
||||
<textarea style={{ width: '100%', height: '50%', border: 'none', outline: 'none', resize: 'none', pointerEvents: 'all' }} value={shape.props.text} onChange={(e) => this.updateProps(shape, { text: e.target.value })} />
|
||||
<ValueInterface type='boolean' value={shape.props.values[currentUser] ?? false} values={shape.props.values} onChange={handleOnChange} />
|
||||
</HTMLContainer>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
private updateProps(shape: ISocialShape, props: Partial<ISocialShape['props']>) {
|
||||
this.editor.updateShape<ISocialShape>({
|
||||
id: shape.id,
|
||||
|
|
@ -70,18 +79,23 @@ export class SocialShapeUtil extends BaseBoxShapeUtil<ISocialShape> {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
// static removeParticipantsNotInRoom(editor: Editor, shapeId: TLShapeId) {
|
||||
// const roomMembers = getRoomMembers(editor)
|
||||
// const _shape = editor.getShape(shapeId)
|
||||
// if (!_shape || _shape.type !== 'gmachine') return
|
||||
// const shape = _shape as IGMachineShape
|
||||
// const participants = new Map(shape.props.participants.map(p => [p.id, p]))
|
||||
// participants.forEach((participant: any) => {
|
||||
// if (!roomMembers.some(rm => rm.id === participant.id)) {
|
||||
// participants.delete(participant.id)
|
||||
// }
|
||||
// })
|
||||
// updateProps(editor, shape, { participants: Array.from(participants.values()) })
|
||||
// }
|
||||
}
|
||||
|
||||
function ValueInterface({ type, value, values, onChange }: { type: ValueType; value: any; values: Record<string, any>; onChange: (value: any) => void }) {
|
||||
switch (type) {
|
||||
case 'boolean':
|
||||
return <>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: '4px' }}>
|
||||
<input style={{ pointerEvents: 'all', width: '20px', height: '20px', margin: 0 }} type="checkbox" value={value} onChange={(e) => onChange(e.target.checked)} />
|
||||
<div style={{ width: '1px', height: '20px', backgroundColor: 'grey' }} />
|
||||
{Object.values(values).map((bool, i) => (
|
||||
<div key={`boolean-${i}`} style={{ backgroundColor: bool ? 'blue' : 'white', width: '20px', height: '20px', border: '1px solid grey', borderRadius: 2 }} />
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
case 'scalar':
|
||||
return <div>Slider</div>
|
||||
default:
|
||||
return <div>No Interface...</div>
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import React from "react";
|
||||
// import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App.tsx";
|
||||
import "./style.css";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
// <React.StrictMode>
|
||||
<App />
|
||||
// </React.StrictMode>,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
import { Editor, RecordId, TLDocument } from "tldraw";
|
||||
|
||||
const docId = 'document:document' as RecordId<TLDocument>
|
||||
|
||||
export function setDocumentMeta(editor: Editor, meta: Record<string, any>) {
|
||||
const updateFunc = (doc: TLDocument) => {
|
||||
return {
|
||||
...doc,
|
||||
meta: {
|
||||
...doc.meta,
|
||||
...meta
|
||||
}
|
||||
}
|
||||
}
|
||||
editor.store.update(docId, updateFunc)
|
||||
}
|
||||
export function getDocumentMeta(editor: Editor) {
|
||||
const document = editor.store.get(docId)
|
||||
if (!document) return
|
||||
return document.meta
|
||||
}
|
||||
export function removeDocumentMeta(editor: Editor, key: string) {
|
||||
const updateFunc = (doc: TLDocument) => {
|
||||
const { [key]: _, ...restMeta } = doc.meta;
|
||||
return {
|
||||
...doc,
|
||||
meta: restMeta
|
||||
};
|
||||
};
|
||||
editor.store.update(docId, updateFunc);
|
||||
}
|
||||
export function getUserId(editor: Editor) {
|
||||
return editor.user.getId()
|
||||
}
|
||||
export function setUserPreferences(editor: Editor, name: string, color: string) {
|
||||
editor.user.updateUserPreferences({
|
||||
name,
|
||||
color
|
||||
})
|
||||
}
|
||||
export function getUsersInRoom(editor: Editor) {
|
||||
return editor.getCollaborators().map(c=> c.userId)
|
||||
}
|
||||
Loading…
Reference in New Issue