pull/3206/head
Mime Čuvalo 2024-03-19 10:28:40 +00:00
rodzic 5edf7dd00d
commit a668447d22
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: BA84499022AC984D
10 zmienionych plików z 190 dodań i 43 usunięć

Wyświetl plik

@ -1592,3 +1592,83 @@ it from receiving any pointer events or affecting the cursor. */
font-size: 12px; font-size: 12px;
font-family: monospace; font-family: monospace;
} }
/* --------------------- Tooltips --------------------- */
.TooltipContent {
border-radius: 4px;
padding: 10px 15px;
font-size: 15px;
line-height: 1;
color: var(--violet-11);
background-color: white;
box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
user-select: none;
animation-duration: 400ms;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
will-change: transform, opacity;
}
.TooltipContent[data-state='delayed-open'][data-side='top'] {
animation-name: slideDownAndFade;
}
.TooltipContent[data-state='delayed-open'][data-side='right'] {
animation-name: slideLeftAndFade;
}
.TooltipContent[data-state='delayed-open'][data-side='bottom'] {
animation-name: slideUpAndFade;
}
.TooltipContent[data-state='delayed-open'][data-side='left'] {
animation-name: slideRightAndFade;
}
.TooltipArrow {
fill: white;
}
div[data-radix-popper-content-wrapper] {
z-index: 200 !important; /* var(--layer-canvas); */
}
@keyframes slideUpAndFade {
from {
opacity: 0;
transform: translateY(2px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideRightAndFade {
from {
opacity: 0;
transform: translateX(-2px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slideDownAndFade {
from {
opacity: 0;
transform: translateY(-2px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideLeftAndFade {
from {
opacity: 0;
transform: translateX(2px);
}
to {
opacity: 1;
transform: translateX(0);
}
}

Wyświetl plik

@ -216,9 +216,9 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
isPrecise: boolean; isPrecise: boolean;
}>; }>;
point: ObjectValidator< { point: ObjectValidator< {
type: "point";
x: number; x: number;
y: number; y: number;
type: "point";
}>; }>;
}, never>; }, never>;
end: UnionValidator<"type", { end: UnionValidator<"type", {
@ -230,9 +230,9 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
isPrecise: boolean; isPrecise: boolean;
}>; }>;
point: ObjectValidator< { point: ObjectValidator< {
type: "point";
x: number; x: number;
y: number; y: number;
type: "point";
}>; }>;
}, never>; }, never>;
bend: Validator<number>; bend: Validator<number>;
@ -1048,9 +1048,9 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
size: EnumStyleProp<"l" | "m" | "s" | "xl">; size: EnumStyleProp<"l" | "m" | "s" | "xl">;
spline: EnumStyleProp<"cubic" | "line">; spline: EnumStyleProp<"cubic" | "line">;
points: DictValidator<string, { points: DictValidator<string, {
id: string;
x: number; x: number;
y: number; y: number;
id: string;
index: IndexKey; index: IndexKey;
}>; }>;
}; };
@ -1112,7 +1112,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
verticalAlign: "end" | "middle" | "start"; verticalAlign: "end" | "middle" | "start";
url: string; url: string;
text: string; text: string;
reacji: Record<string, number> | undefined; reacji: Record<string, string[]> | undefined;
}; };
type: "note"; type: "note";
x: number; x: number;
@ -1137,7 +1137,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
verticalAlign: "end" | "middle" | "start"; verticalAlign: "end" | "middle" | "start";
url: string; url: string;
text: string; text: string;
reacji: Record<string, number> | undefined; reacji: Record<string, string[]> | undefined;
}; };
type: "note"; type: "note";
x: number; x: number;
@ -1154,7 +1154,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
// (undocumented) // (undocumented)
onEditEnd: TLOnEditEndHandler<TLNoteShape>; onEditEnd: TLOnEditEndHandler<TLNoteShape>;
// (undocumented) // (undocumented)
onReacjiSelect: (shape: TLNoteShape, reacji: string, step: number) => void; onReacjiSelect: (shape: TLNoteShape, emoji: string, name: string) => void;
// (undocumented) // (undocumented)
static props: { static props: {
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "yellow">; color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "yellow">;
@ -1165,7 +1165,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
growY: Validator<number>; growY: Validator<number>;
url: Validator<string>; url: Validator<string>;
text: Validator<string>; text: Validator<string>;
reacji: Validator<Record<string, number> | undefined>; reacji: Validator<Record<string, string[]> | undefined>;
}; };
// (undocumented) // (undocumented)
toSvg(shape: TLNoteShape, ctx: SvgExportContext): SVGGElement; toSvg(shape: TLNoteShape, ctx: SvgExportContext): SVGGElement;

Wyświetl plik

@ -1619,7 +1619,7 @@
}, },
{ {
"kind": "Content", "kind": "Content",
"text": "<{\n x: number;\n y: number;\n type: \"point\";\n }>;\n }, never>;\n end: import(\"@tldraw/editor\")." "text": "<{\n type: \"point\";\n x: number;\n y: number;\n }>;\n }, never>;\n end: import(\"@tldraw/editor\")."
}, },
{ {
"kind": "Reference", "kind": "Reference",
@ -1664,7 +1664,7 @@
}, },
{ {
"kind": "Content", "kind": "Content",
"text": "<{\n x: number;\n y: number;\n type: \"point\";\n }>;\n }, never>;\n bend: import(\"@tldraw/editor\")." "text": "<{\n type: \"point\";\n x: number;\n y: number;\n }>;\n }, never>;\n bend: import(\"@tldraw/editor\")."
}, },
{ {
"kind": "Reference", "kind": "Reference",
@ -12361,7 +12361,7 @@
}, },
{ {
"kind": "Content", "kind": "Content",
"text": "<string, {\n id: string;\n x: number;\n y: number;\n index: import(\"@tldraw/editor\")." "text": "<string, {\n x: number;\n y: number;\n id: string;\n index: import(\"@tldraw/editor\")."
}, },
{ {
"kind": "Reference", "kind": "Reference",
@ -13201,7 +13201,7 @@
}, },
{ {
"kind": "Content", "kind": "Content",
"text": "<string, number> | undefined;\n };\n type: \"note\";\n x: number;\n y: number;\n rotation: number;\n index: import(\"@tldraw/editor\")." "text": "<string, string[]> | undefined;\n };\n type: \"note\";\n x: number;\n y: number;\n rotation: number;\n index: import(\"@tldraw/editor\")."
}, },
{ {
"kind": "Reference", "kind": "Reference",
@ -13294,7 +13294,7 @@
}, },
{ {
"kind": "Content", "kind": "Content",
"text": "<string, number> | undefined;\n };\n type: \"note\";\n x: number;\n y: number;\n rotation: number;\n index: import(\"@tldraw/editor\")." "text": "<string, string[]> | undefined;\n };\n type: \"note\";\n x: number;\n y: number;\n rotation: number;\n index: import(\"@tldraw/editor\")."
}, },
{ {
"kind": "Reference", "kind": "Reference",
@ -13413,7 +13413,7 @@
}, },
{ {
"kind": "Content", "kind": "Content",
"text": ", reacji: string, step: number) => void" "text": ", emoji: string, name: string) => void"
}, },
{ {
"kind": "Content", "kind": "Content",
@ -13533,7 +13533,7 @@
}, },
{ {
"kind": "Content", "kind": "Content",
"text": "<string, number> | undefined>;\n }" "text": "<string, string[]> | undefined>;\n }"
}, },
{ {
"kind": "Content", "kind": "Content",

Wyświetl plik

@ -54,6 +54,7 @@
"@radix-ui/react-select": "^1.2.0", "@radix-ui/react-select": "^1.2.0",
"@radix-ui/react-slider": "^1.1.0", "@radix-ui/react-slider": "^1.1.0",
"@radix-ui/react-toast": "^1.1.1", "@radix-ui/react-toast": "^1.1.1",
"@radix-ui/react-tooltip": "^1.0.7",
"@tldraw/editor": "workspace:*", "@tldraw/editor": "workspace:*",
"canvas-size": "^1.2.6", "canvas-size": "^1.2.6",
"classnames": "^2.3.2", "classnames": "^2.3.2",

Wyświetl plik

@ -1,3 +1,4 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { import {
DefaultFontFamilies, DefaultFontFamilies,
Editor, Editor,
@ -10,8 +11,9 @@ import {
noteShapeMigrations, noteShapeMigrations,
noteShapeProps, noteShapeProps,
toDomPrecision, toDomPrecision,
uniq,
} from '@tldraw/editor' } from '@tldraw/editor'
import { useEffect, useRef, useState } from 'react' import { FC, PropsWithChildren, ReactNode, useEffect, useRef, useState } from 'react'
import { Root, createRoot } from 'react-dom/client' import { Root, createRoot } from 'react-dom/client'
import { HyperlinkButton } from '../shared/HyperlinkButton' import { HyperlinkButton } from '../shared/HyperlinkButton'
import { useDefaultColorTheme } from '../shared/ShapeFill' import { useDefaultColorTheme } from '../shared/ShapeFill'
@ -99,7 +101,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
<Reacji <Reacji
editor={this.editor} editor={this.editor}
reacji={reacji} reacji={reacji}
onSelect={(reacji, step) => this.onReacjiSelect(shape, reacji, step)} onSelect={(emoji, name) => this.onReacjiSelect(shape, emoji, name)}
/> />
</div> </div>
{'url' in shape.props && shape.props.url && ( {'url' in shape.props && shape.props.url && (
@ -109,16 +111,22 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
) )
} }
onReacjiSelect = (shape: TLNoteShape, reacji: string, step: number) => { onReacjiSelect = (shape: TLNoteShape, emoji: string, name: string) => {
const newReacji = Object.assign({}, shape.props.reacji) const reacji = Object.assign({}, shape.props.reacji)
newReacji[reacji] = (newReacji[reacji] || 0) + step const includesPersonAlready = reacji[emoji]?.includes(name)
reacji[emoji] = includesPersonAlready
? reacji[emoji].filter((n) => n !== name)
: uniq([...(reacji[emoji] || []), name])
if (!reacji[emoji].length) {
delete reacji[emoji]
}
this.editor.updateShapes([ this.editor.updateShapes([
{ {
...shape, ...shape,
props: { props: {
...shape.props, ...shape.props,
reacji: Object.fromEntries(Object.entries(newReacji).filter(([_, count]) => count > 0)), reacji,
}, },
}, },
]) ])
@ -211,7 +219,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
} }
} }
type ReacjiBagType = { [key: string]: number } type ReacjiBagType = { [key: string]: string[] }
function Reacji({ function Reacji({
editor, editor,
@ -220,10 +228,11 @@ function Reacji({
}: { }: {
editor: Editor editor: Editor
reacji: ReacjiBagType | undefined reacji: ReacjiBagType | undefined
onSelect: (reacji: string, step: number) => void onSelect: (emoji: string, name: string) => void
}) { }) {
const [renderRoot, setRenderRoot] = useState<Root>() const [renderRoot, setRenderRoot] = useState<Root>()
const addButtonRef = useRef<HTMLButtonElement>(null) const addButtonRef = useRef<HTMLButtonElement>(null)
const name = editor.user?.getName() || 'Me'
useEffect(() => { useEffect(() => {
const div = document.createElement('div') const div = document.createElement('div')
@ -239,12 +248,12 @@ function Reacji({
}, []) }, [])
const onEmojiSelect = async (emoji: any) => { const onEmojiSelect = async (emoji: any) => {
onSelect(emoji.native, 1) onSelect(emoji.native, name)
closeMenu() closeMenu()
} }
const onEmojiRetract = (reacji: string) => { const onEmojiRetract = (emoji: string) => {
onSelect(reacji, -1) onSelect(emoji, name)
} }
const closeMenu = () => { const closeMenu = () => {
@ -268,18 +277,41 @@ function Reacji({
return ( return (
<div className="tl-note__reacji"> <div className="tl-note__reacji">
{reacji && {reacji &&
Object.entries(reacji).map(([reacji, count]) => ( Object.entries(reacji).map(([reacji, people]) => (
<button key={reacji} onMouseDown={() => onEmojiRetract(reacji)}> <TooltipWrapper tooltip={people.join(', ')}>
{reacji} <span className="tl-note__reacji-count">{count}</span> <button key={reacji} onMouseDown={() => onEmojiRetract(reacji)}>
</button> {reacji} <span className="tl-note__reacji-count">{people.length}</span>
</button>
</TooltipWrapper>
))} ))}
<button className="tl-note__reacji-add" ref={addButtonRef} onMouseDown={handleOpen}> <TooltipWrapper tooltip="Add emoji">
+ <button className="tl-note__reacji-add" ref={addButtonRef} onMouseDown={handleOpen}>
</button> +
</button>
</TooltipWrapper>
</div> </div>
) )
} }
const TooltipWrapper: FC<PropsWithChildren<{ tooltip: string | ReactNode }>> = ({
children,
tooltip,
}) => {
return (
<Tooltip.Provider delayDuration={200}>
<Tooltip.Root>
<Tooltip.Trigger asChild>{children}</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content className="TooltipContent" sideOffset={5}>
{tooltip}
<Tooltip.Arrow className="TooltipArrow" />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
)
}
function getGrowY(editor: Editor, shape: TLNoteShape, prevGrowY = 0) { function getGrowY(editor: Editor, shape: TLNoteShape, prevGrowY = 0) {
const PADDING = 17 const PADDING = 17

Wyświetl plik

@ -10,6 +10,7 @@ export type EmojiDialogProps = {
left: number left: number
onEmojiSelect: (emoji: any) => void onEmojiSelect: (emoji: any) => void
onClickOutside: () => void onClickOutside: () => void
disableFrequentRows?: boolean
} }
export default track(function EmojiDialog({ export default track(function EmojiDialog({
@ -18,6 +19,7 @@ export default track(function EmojiDialog({
left, left,
onEmojiSelect, onEmojiSelect,
onClickOutside, onClickOutside,
disableFrequentRows,
}: EmojiDialogProps) { }: EmojiDialogProps) {
const isDarkMode = useValue('isDarkMode', () => editor.user.getIsDarkMode(), [editor]) const isDarkMode = useValue('isDarkMode', () => editor.user.getIsDarkMode(), [editor])
const ref = useRef(null) const ref = useRef(null)
@ -33,7 +35,7 @@ export default track(function EmojiDialog({
editor.on('event', eventListener) editor.on('event', eventListener)
instance.current = new Picker({ instance.current = new Picker({
maxFrequentRows: 0, maxFrequentRows: disableFrequentRows ? 0 : undefined,
onEmojiSelect, onEmojiSelect,
onClickOutside, onClickOutside,
theme, theme,
@ -48,7 +50,7 @@ export default track(function EmojiDialog({
EmojiDialogSingleton = null EmojiDialogSingleton = null
editor.off('event', eventListener) editor.off('event', eventListener)
} }
}, [editor, theme, onEmojiSelect, onClickOutside]) }, [editor, theme, onEmojiSelect, onClickOutside, disableFrequentRows])
return ( return (
<div <div

Wyświetl plik

@ -47,9 +47,9 @@ export const arrowShapeProps: {
isPrecise: boolean; isPrecise: boolean;
} & {}>; } & {}>;
point: T.ObjectValidator<{ point: T.ObjectValidator<{
type: "point";
x: number; x: number;
y: number; y: number;
type: "point";
} & {}>; } & {}>;
}, never>; }, never>;
end: T.UnionValidator<"type", { end: T.UnionValidator<"type", {
@ -61,9 +61,9 @@ export const arrowShapeProps: {
isPrecise: boolean; isPrecise: boolean;
} & {}>; } & {}>;
point: T.ObjectValidator<{ point: T.ObjectValidator<{
type: "point";
x: number; x: number;
y: number; y: number;
type: "point";
} & {}>; } & {}>;
}, never>; }, never>;
bend: T.Validator<number>; bend: T.Validator<number>;
@ -684,9 +684,9 @@ export const lineShapeProps: {
size: EnumStyleProp<"l" | "m" | "s" | "xl">; size: EnumStyleProp<"l" | "m" | "s" | "xl">;
spline: EnumStyleProp<"cubic" | "line">; spline: EnumStyleProp<"cubic" | "line">;
points: T.DictValidator<string, { points: T.DictValidator<string, {
id: string;
x: number; x: number;
y: number; y: number;
id: string;
index: IndexKey; index: IndexKey;
} & {}>; } & {}>;
}; };
@ -707,7 +707,7 @@ export const noteShapeProps: {
growY: T.Validator<number>; growY: T.Validator<number>;
url: T.Validator<string>; url: T.Validator<string>;
text: T.Validator<string>; text: T.Validator<string>;
reacji: T.Validator<Record<string, number> | undefined>; reacji: T.Validator<Record<string, string[]> | undefined>;
}; };
// @internal (undocumented) // @internal (undocumented)

Wyświetl plik

@ -364,7 +364,7 @@
}, },
{ {
"kind": "Content", "kind": "Content",
"text": "<{\n x: number;\n y: number;\n type: \"point\";\n } & {}>;\n }, never>;\n end: " "text": "<{\n type: \"point\";\n x: number;\n y: number;\n } & {}>;\n }, never>;\n end: "
}, },
{ {
"kind": "Reference", "kind": "Reference",
@ -409,7 +409,7 @@
}, },
{ {
"kind": "Content", "kind": "Content",
"text": "<{\n x: number;\n y: number;\n type: \"point\";\n } & {}>;\n }, never>;\n bend: " "text": "<{\n type: \"point\";\n x: number;\n y: number;\n } & {}>;\n }, never>;\n bend: "
}, },
{ {
"kind": "Reference", "kind": "Reference",
@ -2818,7 +2818,7 @@
}, },
{ {
"kind": "Content", "kind": "Content",
"text": "<string, {\n id: string;\n x: number;\n y: number;\n index: " "text": "<string, {\n x: number;\n y: number;\n id: string;\n index: "
}, },
{ {
"kind": "Reference", "kind": "Reference",
@ -2972,7 +2972,7 @@
}, },
{ {
"kind": "Content", "kind": "Content",
"text": "<string, number> | undefined>;\n}" "text": "<string, string[]> | undefined>;\n}"
} }
], ],
"fileUrlPath": "packages/tlschema/src/shapes/TLNoteShape.ts", "fileUrlPath": "packages/tlschema/src/shapes/TLNoteShape.ts",

Wyświetl plik

@ -20,7 +20,7 @@ export const noteShapeProps = {
growY: T.positiveNumber, growY: T.positiveNumber,
url: T.linkUrl, url: T.linkUrl,
text: T.string, text: T.string,
reacji: T.dict(T.string, T.positiveInteger).optional(), reacji: T.dict(T.string, T.arrayOf(T.string)).optional(),
} }
/** @public */ /** @public */

Wyświetl plik

@ -5557,6 +5557,37 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@radix-ui/react-tooltip@npm:^1.0.7":
version: 1.0.7
resolution: "@radix-ui/react-tooltip@npm:1.0.7"
dependencies:
"@babel/runtime": "npm:^7.13.10"
"@radix-ui/primitive": "npm:1.0.1"
"@radix-ui/react-compose-refs": "npm:1.0.1"
"@radix-ui/react-context": "npm:1.0.1"
"@radix-ui/react-dismissable-layer": "npm:1.0.5"
"@radix-ui/react-id": "npm:1.0.1"
"@radix-ui/react-popper": "npm:1.1.3"
"@radix-ui/react-portal": "npm:1.0.4"
"@radix-ui/react-presence": "npm:1.0.1"
"@radix-ui/react-primitive": "npm:1.0.3"
"@radix-ui/react-slot": "npm:1.0.2"
"@radix-ui/react-use-controllable-state": "npm:1.0.1"
"@radix-ui/react-visually-hidden": "npm:1.0.3"
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
checksum: 8f075a78db9bfe3dac251266feeb771923176d388c3232f9bad8d85417b5d80d2470697e1c7cae6765d3af16e48552ab9810137c2db193bc37e61b97388e92e8
languageName: node
linkType: hard
"@radix-ui/react-use-callback-ref@npm:1.0.1": "@radix-ui/react-use-callback-ref@npm:1.0.1":
version: 1.0.1 version: 1.0.1
resolution: "@radix-ui/react-use-callback-ref@npm:1.0.1" resolution: "@radix-ui/react-use-callback-ref@npm:1.0.1"
@ -23010,6 +23041,7 @@ __metadata:
"@radix-ui/react-select": "npm:^1.2.0" "@radix-ui/react-select": "npm:^1.2.0"
"@radix-ui/react-slider": "npm:^1.1.0" "@radix-ui/react-slider": "npm:^1.1.0"
"@radix-ui/react-toast": "npm:^1.1.1" "@radix-ui/react-toast": "npm:^1.1.1"
"@radix-ui/react-tooltip": "npm:^1.0.7"
"@testing-library/jest-dom": "npm:^5.16.5" "@testing-library/jest-dom": "npm:^5.16.5"
"@testing-library/react": "npm:^14.0.0" "@testing-library/react": "npm:^14.0.0"
"@tldraw/editor": "workspace:*" "@tldraw/editor": "workspace:*"