[3/3] Highlighter styling (#1490)
This PR finalises the highlighter shape with new colors, sizing, and perfect freehand options. The colors are based on our existing colour palette, but take advantage of wide-gamut displays to make the highlighter highlightier. I used my [oklch color palette tool to pick the palette](https://alex.dytry.ch/toys/palette/?palette=%7B%22families%22:%5B%22black%22,%22grey%22,%22white%22,%22green%22,%22light-green%22,%22blue%22,%22light-blue%22,%22violet%22,%22light-violet%22,%22red%22,%22light-red%22,%22orange%22,%22yellow%22%5D,%22shades%22:%5B%22light-mode%22,%22dark-mode%22,%22hl-light%22,%22hl-dark%22%5D,%22colors%22:%5B%5B%5B0.2308,0,null%5D,%5B0.9097,0,null%5D,%5B0.2308,0,null%5D,%5B0.2308,0,null%5D%5D,%5B%5B0.7692,0.0145,248.02%5D,%5B0.6778,0.0118,256.72%5D,%5B0.7692,0.0145,248.02%5D,%5B0.7692,0.0145,248.02%5D%5D,%5B%5B1,0,null%5D,%5B0.2308,0,null%5D,%5B1,0,null%5D,%5B1,0,null%5D%5D,%5B%5B0.5851,0.1227,164.1%5D,%5B0.5319,0.0811,162.23%5D,%5B0.8729,0.2083,173.3%5D,%5B0.5851,0.152,173.3%5D%5D,%5B%5B0.7146,0.1835,146.44%5D,%5B0.6384,0.1262,143.36%5D,%5B0.8603,0.2438,140.11%5D,%5B0.6082,0.2286,140.11%5D%5D,%5B%5B0.5566,0.2082,268.35%5D,%5B0.4961,0.1644,270.65%5D,%5B0.7158,0.173,243.85%5D,%5B0.5573,0.178,243.85%5D%5D,%5B%5B0.718,0.1422,246.06%5D,%5B0.6366,0.1055,250.98%5D,%5B0.8615,0.1896,200.03%5D,%5B0.707,0.161,200.03%5D%5D,%5B%5B0.5783,0.2186,319.15%5D,%5B0.5043,0.1647,315.37%5D,%5B0.728,0.2001,307.45%5D,%5B0.5433,0.2927,307.45%5D%5D,%5B%5B0.7904,0.1516,319.77%5D,%5B0.6841,0.1139,315.99%5D,%5B0.812,0.21,327.8%5D,%5B0.5668,0.281,327.8%5D%5D,%5B%5B0.5928,0.2106,26.53%5D,%5B0.5112,0.1455,26.18%5D,%5B0.7326,0.21,20.59%5D,%5B0.554,0.2461,20.59%5D%5D,%5B%5B0.7563,0.146,21.1%5D,%5B0.6561,0.0982,20.86%5D,%5B0.7749,0.178,6.8%5D,%5B0.5565,0.2454,6.8%5D%5D,%5B%5B0.6851,0.1954,44.57%5D,%5B0.5958,0.1366,46.6%5D,%5B0.8207,0.175,68.62%5D,%5B0.6567,0.164,68.61%5D%5D,%5B%5B0.8503,0.1149,68.95%5D,%5B0.7404,0.0813,72.25%5D,%5B0.8939,0.2137,100.36%5D,%5B0.7776,0.186,100.36%5D%5D%5D%7D&selected=3). I'm not sure happy about these colors as they are right now - in particular, i think dark mode looks a bit rubbish and there are a few colors where the highlight and original version are much too similar (light-violet & light-red). Black uses yellow (like note shape) and grey uses light-blue. Exports are forced into srgb color space rather than P3 for maximum compatibility. ![image](https://github.com/tldraw/tldraw/assets/1489520/e3de762b-6ef7-4d17-87db-3e2b71dd8de1) ![image](https://github.com/tldraw/tldraw/assets/1489520/3bd90aa9-bdbc-4a2b-9e56-e3a83a2a877b) The size of a highlighter stroke is now based on the text size which works nicely for making the highlighter play well with text: ![image](https://github.com/tldraw/tldraw/assets/1489520/dd3184fc-decd-4db5-90ce-e9cc75edd3d6) Perfect freehands settings are very similar to the draw tool, but with the thinning turned way down. There is still some, but it's pretty minimal. ### The plan 1. initial highlighter shape/tool #1401 2. sandwich rendering for highlighter shapes #1418 3. shape styling - new colours and sizes, lightweight perfect freehand changes #1490 **>you are here<** ### Change Type - [x] `minor` — New Feature ### Test Plan 1. You can find the highlighter tool in the extended toolbar 2. You can activate the highlighter tool by pressing shift-D 3. Highlighter draws nice and vibrantly when over the page background or frame background 4. Highlighter is less vibrant but still visible when drawn over images / other fills 5. Highlighter size should nicely match the corresponding unscaled text size 6. Exports with highlighter look as expected ### Release Notes Highlighter pen is here! 🎉🎉🎉 --------- Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
parent
a11a741de4
commit
d6085e4ea6
23 changed files with 211 additions and 107 deletions
|
@ -1,4 +1,4 @@
|
|||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.5 24.5C8 24 9.3 23 10.5 23C11.3229 23 12.1927 23.1567 12.8195 23.309C13.2406 23.4113 13.6936 23.3064 14 23V23M7.5 24.5L6.5 23.5L5.5 22.5M7.5 24.5L6.5 25.5L3.5 24.5L5.5 22.5M7.5 24.5L5.5 22.5M5.5 22.5C6 22 7 20.7 7 19.5C7 18.6771 6.84326 17.8073 6.69101 17.1805C6.5887 16.7594 6.69357 16.3064 7 16V16M7 16L18.5858 4.41421C19.3668 3.63317 20.6332 3.63317 21.4142 4.41421L25.5858 8.58579C26.3668 9.36684 26.3668 10.6332 25.5858 11.4142L14 23M7 16L14 23" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.5 24.5L5.5 22.5L7.5 24.5L6.5 25.5L3.5 24.5Z" fill="black"/>
|
||||
<path d="M13.1024 25.123L26.6953 10.412C27.424 9.62334 27.3999 8.39987 26.6405 7.64054L23.3649 4.36488C22.6034 3.60337 21.3756 3.58158 20.5875 4.31559L5.98401 17.9175M13.1024 25.123L5.98401 17.9175M13.1024 25.123C12.6024 24.9563 11.3025 24.5374 10.1025 24.5374C8.90254 24.5374 7.60254 25.5374 7.10254 26.0374M5.98401 17.9175C6.15068 18.4175 6.60254 19.8374 6.60254 21.0374C6.60254 22.2374 5.60254 23.5374 5.10254 24.0374M7.10254 26.0374L6.10254 25.0374L5.10254 24.0374M7.10254 26.0374L6.10254 27.0374L2 27.1399L5.10254 24.0374" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M2 26L4 24L6 26L5 27L2 26Z" fill="black"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 719 B After Width: | Height: | Size: 764 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.6041 25.123L27.1506 9.55414L20.0322 2.34865L4.48573 17.9175M11.6041 25.123L7.78596 26.7594L2.84937 21.7357L4.48573 17.9175M11.6041 25.123L4.48573 17.9175" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 352 B |
1
packages/assets/imports.d.ts
vendored
1
packages/assets/imports.d.ts
vendored
|
@ -151,7 +151,6 @@ export function getAssetUrlsByImport(opts?: AssetUrlOptions): {
|
|||
'tool-frame': string
|
||||
'tool-hand': string
|
||||
'tool-highlight': string
|
||||
'tool-highlighter': string
|
||||
'tool-laser': string
|
||||
'tool-line': string
|
||||
'tool-media': string
|
||||
|
|
|
@ -162,7 +162,6 @@ import iconsToolEraser from './icons/icon/tool-eraser.svg'
|
|||
import iconsToolFrame from './icons/icon/tool-frame.svg'
|
||||
import iconsToolHand from './icons/icon/tool-hand.svg'
|
||||
import iconsToolHighlight from './icons/icon/tool-highlight.svg'
|
||||
import iconsToolHighlighter from './icons/icon/tool-highlighter.svg'
|
||||
import iconsToolLaser from './icons/icon/tool-laser.svg'
|
||||
import iconsToolLine from './icons/icon/tool-line.svg'
|
||||
import iconsToolMedia from './icons/icon/tool-media.svg'
|
||||
|
@ -394,7 +393,6 @@ export function getAssetUrlsByImport(opts) {
|
|||
'tool-frame': formatAssetUrl(iconsToolFrame, opts),
|
||||
'tool-hand': formatAssetUrl(iconsToolHand, opts),
|
||||
'tool-highlight': formatAssetUrl(iconsToolHighlight, opts),
|
||||
'tool-highlighter': formatAssetUrl(iconsToolHighlighter, opts),
|
||||
'tool-laser': formatAssetUrl(iconsToolLaser, opts),
|
||||
'tool-line': formatAssetUrl(iconsToolLine, opts),
|
||||
'tool-media': formatAssetUrl(iconsToolMedia, opts),
|
||||
|
|
1
packages/assets/urls.d.ts
vendored
1
packages/assets/urls.d.ts
vendored
|
@ -151,7 +151,6 @@ export function getAssetUrlsByMetaUrl(opts?: AssetUrlOptions): {
|
|||
'tool-frame': string
|
||||
'tool-hand': string
|
||||
'tool-highlight': string
|
||||
'tool-highlighter': string
|
||||
'tool-laser': string
|
||||
'tool-line': string
|
||||
'tool-media': string
|
||||
|
|
|
@ -498,10 +498,6 @@ export function getAssetUrlsByMetaUrl(opts) {
|
|||
new URL('./icons/icon/tool-highlight.svg', import.meta.url).href,
|
||||
opts
|
||||
),
|
||||
'tool-highlighter': formatAssetUrl(
|
||||
new URL('./icons/icon/tool-highlighter.svg', import.meta.url).href,
|
||||
opts
|
||||
),
|
||||
'tool-laser': formatAssetUrl(
|
||||
new URL('./icons/icon/tool-laser.svg', import.meta.url).href,
|
||||
opts
|
||||
|
|
|
@ -634,6 +634,7 @@ export const debugFlags: {
|
|||
logMessages: DebugFlag<never[]>;
|
||||
resetConnectionEveryPing: DebugFlag<boolean>;
|
||||
debugCursors: DebugFlag<boolean>;
|
||||
forceSrgb: DebugFlag<boolean>;
|
||||
};
|
||||
|
||||
// @internal (undocumented)
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
/*
|
||||
https://alex.dytry.ch/toys/palette/?palette=%7B%22families%22:%5B%22black%22,%22grey%22,%22white%22,%22green%22,%22light-green%22,%22blue%22,%22light-blue%22,%22violet%22,%22light-violet%22,%22red%22,%22light-red%22,%22orange%22,%22yellow%22%5D,%22shades%22:%5B%22light-mode%22,%22dark-mode%22,%22hl-light%22,%22hl-dark%22%5D,%22colors%22:%5B%5B%5B0.2308,0,null%5D,%5B0.9097,0,null%5D,%5B0.2308,0,null%5D,%5B0.2308,0,null%5D%5D,%5B%5B0.7692,0.0145,248.02%5D,%5B0.6778,0.0118,256.72%5D,%5B0.7692,0.0145,248.02%5D,%5B0.7692,0.0145,248.02%5D%5D,%5B%5B1,0,null%5D,%5B0.2308,0,null%5D,%5B1,0,null%5D,%5B1,0,null%5D%5D,%5B%5B0.5851,0.1227,164.1%5D,%5B0.5319,0.0811,162.23%5D,%5B0.8729,0.2083,173.3%5D,%5B0.5851,0.152,173.3%5D%5D,%5B%5B0.7146,0.1835,146.44%5D,%5B0.6384,0.1262,143.36%5D,%5B0.8603,0.2438,140.11%5D,%5B0.6082,0.2286,140.11%5D%5D,%5B%5B0.5566,0.2082,268.35%5D,%5B0.4961,0.1644,270.65%5D,%5B0.7158,0.173,243.85%5D,%5B0.5573,0.178,243.85%5D%5D,%5B%5B0.718,0.1422,246.06%5D,%5B0.6366,0.1055,250.98%5D,%5B0.8615,0.1896,200.03%5D,%5B0.707,0.161,200.03%5D%5D,%5B%5B0.5783,0.2186,319.15%5D,%5B0.5043,0.1647,315.37%5D,%5B0.728,0.2001,307.45%5D,%5B0.5433,0.2927,307.45%5D%5D,%5B%5B0.7904,0.1516,319.77%5D,%5B0.6841,0.1139,315.99%5D,%5B0.812,0.21,327.8%5D,%5B0.5668,0.281,327.8%5D%5D,%5B%5B0.5928,0.2106,26.53%5D,%5B0.5112,0.1455,26.18%5D,%5B0.7326,0.21,20.59%5D,%5B0.554,0.2461,20.59%5D%5D,%5B%5B0.7563,0.146,21.1%5D,%5B0.6561,0.0982,20.86%5D,%5B0.7749,0.178,6.8%5D,%5B0.5565,0.2454,6.8%5D%5D,%5B%5B0.6851,0.1954,44.57%5D,%5B0.5958,0.1366,46.6%5D,%5B0.8207,0.175,68.62%5D,%5B0.6567,0.164,68.61%5D%5D,%5B%5B0.8503,0.1149,68.95%5D,%5B0.7404,0.0813,72.25%5D,%5B0.8939,0.2137,100.36%5D,%5B0.7776,0.186,100.36%5D%5D%5D%7D&selected=3
|
||||
*/
|
||||
|
||||
.tl-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -142,6 +146,21 @@
|
|||
--palette-violet-pattern: #bd63d3;
|
||||
--palette-white-pattern: #ffffff;
|
||||
--palette-yellow-pattern: #fecb92;
|
||||
|
||||
/* for highlighter pen */
|
||||
--palette-black-highlight: #fddd00;
|
||||
--palette-grey-highlight: #cbe7f1;
|
||||
--palette-green-highlight: #00ffc8;
|
||||
--palette-light-green-highlight: #65f641;
|
||||
--palette-blue-highlight: #10acff;
|
||||
--palette-light-blue-highlight: #00f4ff;
|
||||
--palette-violet-highlight: #c77cff;
|
||||
--palette-light-violet-highlight: #ff88ff;
|
||||
--palette-red-highlight: #ff636e;
|
||||
--palette-light-red-highlight: #ff7fa3;
|
||||
--palette-orange-highlight: #ffa500;
|
||||
--palette-yellow-highlight: #fddd00;
|
||||
|
||||
--shadow-1: 0px 1px 2px rgba(0, 0, 0, 0.22), 0px 1px 3px rgba(0, 0, 0, 0.09);
|
||||
--shadow-2: 0px 0px 2px rgba(0, 0, 0, 0.12), 0px 2px 3px rgba(0, 0, 0, 0.24),
|
||||
0px 2px 6px rgba(0, 0, 0, 0.1), inset 0px 0px 0px 1px var(--color-panel-contrast);
|
||||
|
@ -210,6 +229,7 @@
|
|||
--palette-violet-semi: #31293c;
|
||||
--palette-white-semi: #ffffff;
|
||||
--palette-yellow-semi: #3c3934;
|
||||
|
||||
/* for fill style 'pattern' */
|
||||
--palette-black-pattern: #989898;
|
||||
--palette-blue-pattern: #3a4b9e;
|
||||
|
@ -224,6 +244,21 @@
|
|||
--palette-violet-pattern: #763a8b;
|
||||
--palette-white-pattern: #ffffff;
|
||||
--palette-yellow-pattern: #fecb92;
|
||||
|
||||
/* for highlighter pen */
|
||||
--palette-black-highlight: #d2b700;
|
||||
--palette-grey-highlight: #9cb4cb;
|
||||
--palette-green-highlight: #009774;
|
||||
--palette-light-green-highlight: #00a000;
|
||||
--palette-blue-highlight: #0079d2;
|
||||
--palette-light-blue-highlight: #00bdc8;
|
||||
--palette-violet-highlight: #9e00ee;
|
||||
--palette-light-violet-highlight: #c400c7;
|
||||
--palette-red-highlight: #de002c;
|
||||
--palette-light-red-highlight: #db005b;
|
||||
--palette-orange-highlight: #d07a00;
|
||||
--palette-yellow-highlight: #d2b700;
|
||||
|
||||
--shadow-1: 0px 1px 2px #00000029, 0px 1px 3px #00000038,
|
||||
inset 0px 0px 0px 1px var(--color-panel-contrast);
|
||||
--shadow-2: 0px 1px 3px #00000077, 0px 2px 6px #00000055,
|
||||
|
@ -232,6 +267,41 @@
|
|||
inset 0px 0px 0px 1px var(--color-panel-contrast);
|
||||
}
|
||||
|
||||
/** p3 colors */
|
||||
@media (color-gamut: p3) {
|
||||
.tl-theme__light:not(.tl-theme__force-sRGB) {
|
||||
/* for highlighter pen */
|
||||
--palette-black-highlight: color(display-p3 0.972 0.8705 0.05);
|
||||
--palette-grey-highlight: color(display-p3 0.8163 0.9023 0.9416);
|
||||
--palette-green-highlight: color(display-p3 0.2536 0.984 0.7981);
|
||||
--palette-light-green-highlight: color(display-p3 0.563 0.9495 0.3857);
|
||||
--palette-blue-highlight: color(display-p3 0.308 0.6632 0.9996);
|
||||
--palette-light-blue-highlight: color(display-p3 0.1512 0.9414 0.9996);
|
||||
--palette-violet-highlight: color(display-p3 0.7469 0.5089 0.9995);
|
||||
--palette-light-violet-highlight: color(display-p3 0.9676 0.5652 0.9999);
|
||||
--palette-red-highlight: color(display-p3 0.9992 0.4376 0.45);
|
||||
--palette-light-red-highlight: color(display-p3 0.9988 0.5301 0.6397);
|
||||
--palette-orange-highlight: color(display-p3 0.9988 0.6905 0.266);
|
||||
--palette-yellow-highlight: color(display-p3 0.972 0.8705 0.05);
|
||||
}
|
||||
|
||||
.tl-theme__dark:not(.tl-theme__force-sRGB) {
|
||||
/* for highlighter pen */
|
||||
--palette-black-highlight: color(display-p3 0.8078 0.7225 0.0312);
|
||||
--palette-grey-highlight: color(display-p3 0.6299 0.7012 0.7856);
|
||||
--palette-green-highlight: color(display-p3 0.0085 0.582 0.4604);
|
||||
--palette-light-green-highlight: color(display-p3 0.2711 0.6172 0.0195);
|
||||
--palette-blue-highlight: color(display-p3 0.0032 0.4655 0.7991);
|
||||
--palette-light-blue-highlight: color(display-p3 0.0023 0.7259 0.7735);
|
||||
--palette-violet-highlight: color(display-p3 0.5651 0.0079 0.8986);
|
||||
--palette-light-violet-highlight: color(display-p3 0.7024 0.0403 0.753);
|
||||
--palette-red-highlight: color(display-p3 0.7978 0.0509 0.2035);
|
||||
--palette-light-red-highlight: color(display-p3 0.7849 0.0585 0.3589);
|
||||
--palette-orange-highlight: color(display-p3 0.7699 0.4937 0.0085);
|
||||
--palette-yellow-highlight: color(display-p3 0.8078 0.7225 0.0312);
|
||||
}
|
||||
}
|
||||
|
||||
.tl-counter-scaled {
|
||||
transform: scale(var(--tl-scale));
|
||||
transform-origin: top left;
|
||||
|
|
|
@ -5617,7 +5617,9 @@ export class App extends EventEmitter<TLEventMap> {
|
|||
// Get the styles from the container. We'll use these to pull out colors etc.
|
||||
// NOTE: We can force force a light theme here becasue we don't want export
|
||||
const fakeContainerEl = document.createElement('div')
|
||||
fakeContainerEl.className = `tl-container tl-theme__${darkMode ? 'dark' : 'light'}`
|
||||
fakeContainerEl.className = `tl-container tl-theme__${
|
||||
darkMode ? 'dark' : 'light'
|
||||
} tl-theme__force-sRGB`
|
||||
document.body.appendChild(fakeContainerEl)
|
||||
|
||||
const containerStyle = getComputedStyle(fakeContainerEl)
|
||||
|
@ -5642,6 +5644,12 @@ export class App extends EventEmitter<TLEventMap> {
|
|||
containerStyle.getPropertyValue(`--palette-${color.id}-semi`),
|
||||
])
|
||||
) as Record<TLColorType, string>,
|
||||
highlight: Object.fromEntries(
|
||||
STYLES.color.map((color) => [
|
||||
color.id,
|
||||
containerStyle.getPropertyValue(`--palette-${color.id}-highlight`),
|
||||
])
|
||||
) as Record<TLColorType, string>,
|
||||
text: containerStyle.getPropertyValue(`--color-text`),
|
||||
background: containerStyle.getPropertyValue(`--color-background`),
|
||||
solid: containerStyle.getPropertyValue(`--palette-solid`),
|
||||
|
|
|
@ -23,10 +23,10 @@ import { getDrawShapeStrokeDashArray, getFreehandOptions, getPointsFromSegments
|
|||
export class TLDrawUtil extends TLShapeUtil<TLDrawShape> {
|
||||
static override type = 'draw'
|
||||
|
||||
hideResizeHandles = (shape: TLDrawShape) => this.getIsDot(shape)
|
||||
hideRotateHandle = (shape: TLDrawShape) => this.getIsDot(shape)
|
||||
hideSelectionBoundsBg = (shape: TLDrawShape) => this.getIsDot(shape)
|
||||
hideSelectionBoundsFg = (shape: TLDrawShape) => this.getIsDot(shape)
|
||||
hideResizeHandles = (shape: TLDrawShape) => getIsDot(shape)
|
||||
hideRotateHandle = (shape: TLDrawShape) => getIsDot(shape)
|
||||
hideSelectionBoundsBg = (shape: TLDrawShape) => getIsDot(shape)
|
||||
hideSelectionBoundsFg = (shape: TLDrawShape) => getIsDot(shape)
|
||||
|
||||
override defaultProps(): TLDrawShape['props'] {
|
||||
return {
|
||||
|
@ -44,10 +44,6 @@ export class TLDrawUtil extends TLShapeUtil<TLDrawShape> {
|
|||
|
||||
isClosed = (shape: TLDrawShape) => shape.props.isClosed
|
||||
|
||||
private getIsDot(shape: TLDrawShape) {
|
||||
return shape.props.segments.length === 1 && shape.props.segments[0].points.length < 2
|
||||
}
|
||||
|
||||
getBounds(shape: TLDrawShape) {
|
||||
return Box2d.FromPoints(this.outline(shape))
|
||||
}
|
||||
|
@ -310,3 +306,7 @@ function getDot(point: VecLike, sw: number) {
|
|||
r * 2
|
||||
},0`
|
||||
}
|
||||
|
||||
function getIsDot(shape: TLDrawShape) {
|
||||
return shape.props.segments.length === 1 && shape.props.segments[0].points.length < 2
|
||||
}
|
||||
|
|
|
@ -36,6 +36,21 @@ const solidSettings = (strokeWidth: number): StrokeOptions => {
|
|||
}
|
||||
}
|
||||
|
||||
export function getHighlightFreehandSettings(
|
||||
strokeWidth: number,
|
||||
showAsComplete: boolean
|
||||
): StrokeOptions {
|
||||
return {
|
||||
size: 1 + strokeWidth,
|
||||
thinning: 0.1,
|
||||
streamline: 0.1, // 0.62 + ((1 + strokeWidth) / 8) * 0.06,
|
||||
smoothing: 0.5,
|
||||
simulatePressure: true,
|
||||
easing: EASINGS.easeOutSine,
|
||||
last: showAsComplete,
|
||||
}
|
||||
}
|
||||
|
||||
export function getFreehandOptions(
|
||||
shapeProps: { dash: TLDashType; isPen: boolean; isComplete: boolean },
|
||||
strokeWidth: number,
|
||||
|
|
|
@ -11,24 +11,25 @@ import {
|
|||
import { TLDrawShapeSegment, TLHighlightShape } from '@tldraw/tlschema'
|
||||
import { last, rng } from '@tldraw/utils'
|
||||
import { SVGContainer } from '../../../components/SVGContainer'
|
||||
import { FONT_SIZES } from '../../../constants'
|
||||
import { getSvgPathFromStroke, getSvgPathFromStrokePoints } from '../../../utils/svg'
|
||||
import { App } from '../../App'
|
||||
import { ShapeFill } from '../shared/ShapeFill'
|
||||
import { TLExportColors } from '../shared/TLExportColors'
|
||||
import { useForceSolid } from '../shared/useForceSolid'
|
||||
import { getFreehandOptions, getPointsFromSegments } from '../TLDrawUtil/getPath'
|
||||
import { getHighlightFreehandSettings, getPointsFromSegments } from '../TLDrawUtil/getPath'
|
||||
import { OnResizeHandler, TLShapeUtil } from '../TLShapeUtil'
|
||||
|
||||
const OVERLAY_OPACITY = 0.4
|
||||
const OVERLAY_OPACITY = 0.35
|
||||
const UNDERLAY_OPACITY = 0.82
|
||||
|
||||
/** @public */
|
||||
export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
|
||||
static type = 'highlight'
|
||||
|
||||
hideResizeHandles = (shape: TLHighlightShape) => this.getIsDot(shape)
|
||||
hideRotateHandle = (shape: TLHighlightShape) => this.getIsDot(shape)
|
||||
hideSelectionBoundsBg = (shape: TLHighlightShape) => this.getIsDot(shape)
|
||||
hideSelectionBoundsFg = (shape: TLHighlightShape) => this.getIsDot(shape)
|
||||
hideResizeHandles = (shape: TLHighlightShape) => getIsDot(shape)
|
||||
hideRotateHandle = (shape: TLHighlightShape) => getIsDot(shape)
|
||||
hideSelectionBoundsBg = (shape: TLHighlightShape) => getIsDot(shape)
|
||||
hideSelectionBoundsFg = (shape: TLHighlightShape) => getIsDot(shape)
|
||||
|
||||
override defaultProps(): TLHighlightShape['props'] {
|
||||
return {
|
||||
|
@ -41,10 +42,6 @@ export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
|
|||
}
|
||||
}
|
||||
|
||||
private getIsDot(shape: TLHighlightShape) {
|
||||
return shape.props.segments.length === 1 && shape.props.segments[0].points.length < 2
|
||||
}
|
||||
|
||||
getBounds(shape: TLHighlightShape) {
|
||||
return Box2d.FromPoints(this.outline(shape))
|
||||
}
|
||||
|
@ -60,7 +57,7 @@ export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
|
|||
hitTestPoint(shape: TLHighlightShape, point: VecLike): boolean {
|
||||
const outline = this.outline(shape)
|
||||
const zoomLevel = this.app.zoomLevel
|
||||
const offsetDist = this.app.getStrokeWidth(shape.props.size) / zoomLevel
|
||||
const offsetDist = getStrokeWidth(shape) / zoomLevel
|
||||
|
||||
if (shape.props.segments.length === 1 && shape.props.segments[0].points.length < 4) {
|
||||
if (shape.props.segments[0].points.some((pt) => Vec2d.Dist(point, pt) < offsetDist * 1.5)) {
|
||||
|
@ -85,7 +82,7 @@ export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
|
|||
|
||||
if (shape.props.segments.length === 1 && shape.props.segments[0].points.length < 4) {
|
||||
const zoomLevel = this.app.zoomLevel
|
||||
const offsetDist = this.app.getStrokeWidth(shape.props.size) / zoomLevel
|
||||
const offsetDist = getStrokeWidth(shape) / zoomLevel
|
||||
|
||||
if (
|
||||
shape.props.segments[0].points.some(
|
||||
|
@ -106,16 +103,28 @@ export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
|
|||
}
|
||||
|
||||
render(shape: TLHighlightShape) {
|
||||
return <HighlightRenderer app={this.app} shape={shape} opacity={OVERLAY_OPACITY} />
|
||||
return (
|
||||
<HighlightRenderer
|
||||
strokeWidth={getStrokeWidth(shape)}
|
||||
shape={shape}
|
||||
opacity={OVERLAY_OPACITY}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
renderBackground(shape: TLHighlightShape) {
|
||||
return <HighlightRenderer app={this.app} shape={shape} />
|
||||
return (
|
||||
<HighlightRenderer
|
||||
strokeWidth={getStrokeWidth(shape)}
|
||||
shape={shape}
|
||||
opacity={UNDERLAY_OPACITY}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
indicator(shape: TLHighlightShape) {
|
||||
const forceSolid = useForceSolid()
|
||||
const strokeWidth = this.app.getStrokeWidth(shape.props.size)
|
||||
const strokeWidth = getStrokeWidth(shape)
|
||||
const allPointsFromSegments = getPointsFromSegments(shape.props.segments)
|
||||
|
||||
let sw = strokeWidth
|
||||
|
@ -124,27 +133,33 @@ export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
|
|||
}
|
||||
|
||||
const showAsComplete = shape.props.isComplete || last(shape.props.segments)?.type === 'straight'
|
||||
const options = getFreehandOptions(
|
||||
{ dash: 'draw', isComplete: shape.props.isComplete, isPen: shape.props.isPen },
|
||||
sw,
|
||||
showAsComplete,
|
||||
true
|
||||
)
|
||||
const options = getHighlightFreehandSettings(strokeWidth, showAsComplete)
|
||||
const strokePoints = getStrokePoints(allPointsFromSegments, options)
|
||||
const solidStrokePath =
|
||||
strokePoints.length > 1
|
||||
? getSvgPathFromStrokePoints(strokePoints, false)
|
||||
: getDot(allPointsFromSegments[0], sw)
|
||||
|
||||
return <path d={solidStrokePath} />
|
||||
let strokePath
|
||||
if (strokePoints.length < 2) {
|
||||
strokePath = getIndicatorDot(allPointsFromSegments[0], sw)
|
||||
} else {
|
||||
strokePath = getSvgPathFromStrokePoints(strokePoints, false)
|
||||
}
|
||||
|
||||
return <path d={strokePath} />
|
||||
}
|
||||
|
||||
toSvg(shape: TLHighlightShape, _font: string | undefined, colors: TLExportColors) {
|
||||
return highlighterToSvg(this.app, shape, OVERLAY_OPACITY, colors)
|
||||
override expandSelectionOutlinePx(shape: TLHighlightShape): number {
|
||||
return getStrokeWidth(shape) / 2
|
||||
}
|
||||
|
||||
toBackgroundSvg(shape: TLHighlightShape, font: string | undefined, colors: TLExportColors) {
|
||||
return highlighterToSvg(this.app, shape, 1, colors)
|
||||
override toSvg(shape: TLHighlightShape, _font: string | undefined, colors: TLExportColors) {
|
||||
return highlighterToSvg(getStrokeWidth(shape), shape, OVERLAY_OPACITY, colors)
|
||||
}
|
||||
|
||||
override toBackgroundSvg(
|
||||
shape: TLHighlightShape,
|
||||
font: string | undefined,
|
||||
colors: TLExportColors
|
||||
) {
|
||||
return highlighterToSvg(getStrokeWidth(shape), shape, UNDERLAY_OPACITY, colors)
|
||||
}
|
||||
|
||||
override onResize: OnResizeHandler<TLHighlightShape> = (shape, info) => {
|
||||
|
@ -171,30 +186,32 @@ export class TLHighlightUtil extends TLShapeUtil<TLHighlightShape> {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
expandSelectionOutlinePx(shape: TLHighlightShape): number {
|
||||
return (this.app.getStrokeWidth(shape.props.size) * 1.6) / 2
|
||||
}
|
||||
}
|
||||
|
||||
function getDot(point: VecLike, sw: number) {
|
||||
const r = (sw + 1) * 0.5
|
||||
function getShapeDot(point: VecLike) {
|
||||
const r = 0.1
|
||||
return `M ${point.x} ${point.y} m -${r}, 0 a ${r},${r} 0 1,0 ${r * 2},0 a ${r},${r} 0 1,0 -${
|
||||
r * 2
|
||||
},0`
|
||||
}
|
||||
|
||||
function getIndicatorDot(point: VecLike, sw: number) {
|
||||
const r = sw / 2
|
||||
return `M ${point.x} ${point.y} m -${r}, 0 a ${r},${r} 0 1,0 ${r * 2},0 a ${r},${r} 0 1,0 -${
|
||||
r * 2
|
||||
},0`
|
||||
}
|
||||
|
||||
function HighlightRenderer({
|
||||
app,
|
||||
strokeWidth,
|
||||
shape,
|
||||
opacity,
|
||||
}: {
|
||||
app: App
|
||||
strokeWidth: number
|
||||
shape: TLHighlightShape
|
||||
opacity?: number
|
||||
}) {
|
||||
const forceSolid = useForceSolid()
|
||||
const strokeWidth = app.getStrokeWidth(shape.props.size)
|
||||
const allPointsFromSegments = getPointsFromSegments(shape.props.segments)
|
||||
|
||||
const showAsComplete = shape.props.isComplete || last(shape.props.segments)?.type === 'straight'
|
||||
|
@ -204,30 +221,27 @@ function HighlightRenderer({
|
|||
sw += rng(shape.id)() * (strokeWidth / 6)
|
||||
}
|
||||
|
||||
const options = getFreehandOptions(
|
||||
{ isComplete: shape.props.isComplete, isPen: shape.props.isPen, dash: 'draw' },
|
||||
sw,
|
||||
showAsComplete,
|
||||
forceSolid
|
||||
)
|
||||
const options = getHighlightFreehandSettings(sw, showAsComplete)
|
||||
|
||||
const strokePoints = getStrokePoints(allPointsFromSegments, options)
|
||||
|
||||
const solidStrokePath =
|
||||
strokePoints.length > 1
|
||||
? getSvgPathFromStrokePoints(strokePoints, false)
|
||||
: getDot(allPointsFromSegments[0], sw)
|
||||
: getShapeDot(allPointsFromSegments[0])
|
||||
|
||||
if (!forceSolid || strokePoints.length < 2) {
|
||||
setStrokePointRadii(strokePoints, options)
|
||||
const strokeOutlinePoints = getStrokeOutlinePoints(strokePoints, options)
|
||||
|
||||
return (
|
||||
<SVGContainer id={shape.id} style={{ opacity }}>
|
||||
<ShapeFill fill="none" color={shape.props.color} d={solidStrokePath} />
|
||||
<path
|
||||
d={getSvgPathFromStroke(strokeOutlinePoints, true)}
|
||||
d={solidStrokePath}
|
||||
strokeLinecap="round"
|
||||
fill="currentColor"
|
||||
fill="none"
|
||||
pointerEvents="all"
|
||||
stroke={`var(--palette-${shape.props.color}-highlight)`}
|
||||
strokeWidth={sw}
|
||||
/>
|
||||
</SVGContainer>
|
||||
)
|
||||
|
@ -240,7 +254,7 @@ function HighlightRenderer({
|
|||
d={solidStrokePath}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke={`var(--palette-${shape.props.color}-highlight)`}
|
||||
strokeWidth={strokeWidth}
|
||||
strokeDashoffset="0"
|
||||
/>
|
||||
|
@ -249,14 +263,13 @@ function HighlightRenderer({
|
|||
}
|
||||
|
||||
function highlighterToSvg(
|
||||
app: App,
|
||||
strokeWidth: number,
|
||||
shape: TLHighlightShape,
|
||||
opacity: number,
|
||||
colors: TLExportColors
|
||||
) {
|
||||
const { color } = shape.props
|
||||
|
||||
const strokeWidth = app.getStrokeWidth(shape.props.size)
|
||||
const allPointsFromSegments = getPointsFromSegments(shape.props.segments)
|
||||
|
||||
const showAsComplete = shape.props.isComplete || last(shape.props.segments)?.type === 'straight'
|
||||
|
@ -266,12 +279,7 @@ function highlighterToSvg(
|
|||
sw += rng(shape.id)() * (strokeWidth / 6)
|
||||
}
|
||||
|
||||
const options = getFreehandOptions(
|
||||
{ dash: 'draw', isComplete: shape.props.isComplete, isPen: shape.props.isPen },
|
||||
sw,
|
||||
showAsComplete,
|
||||
false
|
||||
)
|
||||
const options = getHighlightFreehandSettings(sw, showAsComplete)
|
||||
const strokePoints = getStrokePoints(allPointsFromSegments, options)
|
||||
|
||||
setStrokePointRadii(strokePoints, options)
|
||||
|
@ -279,9 +287,17 @@ function highlighterToSvg(
|
|||
|
||||
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
|
||||
path.setAttribute('d', getSvgPathFromStroke(strokeOutlinePoints, true))
|
||||
path.setAttribute('fill', colors.fill[color])
|
||||
path.setAttribute('fill', colors.highlight[color])
|
||||
path.setAttribute('stroke-linecap', 'round')
|
||||
path.setAttribute('opacity', opacity.toString())
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
function getStrokeWidth(shape: TLHighlightShape) {
|
||||
return FONT_SIZES[shape.props.size] * 1.12
|
||||
}
|
||||
|
||||
function getIsDot(shape: TLHighlightShape) {
|
||||
return shape.props.segments.length === 1 && shape.props.segments[0].points.length < 2
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ export type TLExportColors = {
|
|||
fill: Record<TLColorType, string>
|
||||
pattern: Record<TLColorType, string>
|
||||
semi: Record<TLColorType, string>
|
||||
highlight: Record<TLColorType, string>
|
||||
solid: string
|
||||
text: string
|
||||
background: string
|
||||
|
|
|
@ -2,11 +2,11 @@ import { TLEventHandlers } from '../types/event-types'
|
|||
import { StateNode } from './StateNode'
|
||||
import { TLArrowTool } from './TLArrowTool/TLArrowTool'
|
||||
import { TLDrawTool } from './TLDrawTool/TLDrawTool'
|
||||
import { TLHighlightTool } from './TLDrawTool/TLHighlightTool'
|
||||
import { TLEraserTool } from './TLEraserTool/TLEraserTool'
|
||||
import { TLFrameTool } from './TLFrameTool/TLFrameTool'
|
||||
import { TLGeoTool } from './TLGeoTool/TLGeoTool'
|
||||
import { TLHandTool } from './TLHandTool/TLHandTool'
|
||||
import { TLHighlightTool } from './TLHighlightTool/TLHighlightTool'
|
||||
import { TLLaserTool } from './TLLaserTool/TLLaserTool'
|
||||
import { TLLineTool } from './TLLineTool/TLLineTool'
|
||||
import { TLNoteTool } from './TLNoteTool/TLNoteTool'
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { TLStyleType } from '@tldraw/tlschema'
|
||||
import { StateNode } from '../StateNode'
|
||||
|
||||
import { Drawing } from './children/Drawing'
|
||||
import { Idle } from './children/Idle'
|
||||
// shared custody
|
||||
import { Drawing } from '../TLDrawTool/children/Drawing'
|
||||
import { Idle } from '../TLDrawTool/children/Idle'
|
||||
|
||||
export class TLHighlightTool extends StateNode {
|
||||
static override id = 'highlight'
|
|
@ -116,9 +116,7 @@ export const Shape = track(function Shape({
|
|||
data-shape-type={shape.type}
|
||||
draggable={false}
|
||||
>
|
||||
{isCulled ? (
|
||||
<CulledShape shape={shape} util={util} />
|
||||
) : (
|
||||
{!isCulled && (
|
||||
<OptionalErrorBoundary
|
||||
fallback={ShapeErrorFallback ? (error) => <ShapeErrorFallback error={error} /> : null}
|
||||
onError={(error) =>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react'
|
||||
import { useValue } from 'signia-react'
|
||||
import { debugFlags } from '../utils/debug-flags'
|
||||
import { useApp } from './useApp'
|
||||
import { useContainer } from './useContainer'
|
||||
|
||||
|
@ -7,6 +8,7 @@ export function useDarkMode() {
|
|||
const app = useApp()
|
||||
const container = useContainer()
|
||||
const isDarkMode = useValue('isDarkMode', () => app.isDarkMode, [app])
|
||||
const forceSrgb = useValue(debugFlags.forceSrgb)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isDarkMode) {
|
||||
|
@ -24,5 +26,10 @@ export function useDarkMode() {
|
|||
color: 'black',
|
||||
})
|
||||
}
|
||||
}, [app, container, isDarkMode])
|
||||
if (forceSrgb) {
|
||||
container.classList.add('tl-theme__force-sRGB')
|
||||
} else {
|
||||
container.classList.remove('tl-theme__force-sRGB')
|
||||
}
|
||||
}, [app, container, forceSrgb, isDarkMode])
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ export const featureFlags = {
|
|||
// todo: remove this. it's not used, but we only have one feature flag and i
|
||||
// wanted an example :(
|
||||
peopleMenu: createFeatureFlag('peopleMenu'),
|
||||
highlighterTool: createFeatureFlag('highlighterTool'),
|
||||
highlighterTool: createFeatureFlag('highlighterTool', { all: true }),
|
||||
} satisfies Record<string, DebugFlag<boolean>>
|
||||
|
||||
/** @internal */
|
||||
|
@ -51,6 +51,7 @@ export const debugFlags = {
|
|||
debugCursors: createDebugValue('debugCursors', {
|
||||
defaults: { all: false },
|
||||
}),
|
||||
forceSrgb: createDebugValue('forceSrgbColors', { defaults: { all: false } }),
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
|
|
@ -732,10 +732,10 @@ export type TLUiEventHandler<T extends keyof TLUiEventMap = keyof TLUiEventMap>
|
|||
export type TLUiEventSource = 'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'export-menu' | 'help-menu' | 'helper-buttons' | 'kbd' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'share-menu' | 'toolbar' | 'unknown' | 'zoom-menu';
|
||||
|
||||
// @public (undocumented)
|
||||
export type TLUiIconType = 'align-bottom-center' | 'align-bottom-left' | 'align-bottom-right' | 'align-bottom' | 'align-center-center' | 'align-center-horizontal' | 'align-center-left' | 'align-center-right' | 'align-center-vertical' | 'align-left' | 'align-right' | 'align-top-center' | 'align-top-left' | 'align-top-right' | 'align-top' | 'arrow-left' | 'arrowhead-arrow' | 'arrowhead-bar' | 'arrowhead-diamond' | 'arrowhead-dot' | 'arrowhead-none' | 'arrowhead-square' | 'arrowhead-triangle-inverted' | 'arrowhead-triangle' | 'aspect-ratio' | 'avatar' | 'blob' | 'bring-forward' | 'bring-to-front' | 'check' | 'checkbox-checked' | 'checkbox-empty' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'chevrons-ne' | 'chevrons-sw' | 'clipboard-copied' | 'clipboard-copy' | 'code' | 'collab' | 'color' | 'comment' | 'cross-2' | 'cross' | 'dash-dashed' | 'dash-dotted' | 'dash-draw' | 'dash-solid' | 'discord' | 'distribute-horizontal' | 'distribute-vertical' | 'dot' | 'dots-horizontal' | 'dots-vertical' | 'drag-handle-dots' | 'duplicate' | 'edit' | 'external-link' | 'file' | 'fill-none' | 'fill-pattern' | 'fill-semi' | 'fill-solid' | 'follow' | 'following' | 'font-draw' | 'font-mono' | 'font-sans' | 'font-serif' | 'geo-arrow-down' | 'geo-arrow-left' | 'geo-arrow-right' | 'geo-arrow-up' | 'geo-check-box' | 'geo-diamond' | 'geo-ellipse' | 'geo-hexagon' | 'geo-octagon' | 'geo-oval' | 'geo-pentagon' | 'geo-rectangle' | 'geo-rhombus-2' | 'geo-rhombus' | 'geo-star' | 'geo-trapezoid' | 'geo-triangle' | 'geo-x-box' | 'github' | 'group' | 'hidden' | 'image' | 'info-circle' | 'leading' | 'link' | 'lock-small' | 'lock' | 'menu' | 'minus' | 'mixed' | 'pack' | 'page' | 'plus' | 'question-mark-circle' | 'question-mark' | 'redo' | 'reset-zoom' | 'rotate-ccw' | 'rotate-cw' | 'ruler' | 'search' | 'send-backward' | 'send-to-back' | 'settings-horizontal' | 'settings-vertical-1' | 'settings-vertical' | 'share-1' | 'share-2' | 'size-extra-large' | 'size-large' | 'size-medium' | 'size-small' | 'spline-cubic' | 'spline-line' | 'stack-horizontal' | 'stack-vertical' | 'stretch-horizontal' | 'stretch-vertical' | 'text-align-center' | 'text-align-justify' | 'text-align-left' | 'text-align-right' | 'tool-arrow' | 'tool-embed' | 'tool-eraser' | 'tool-frame' | 'tool-hand' | 'tool-highlight' | 'tool-highlighter' | 'tool-laser' | 'tool-line' | 'tool-media' | 'tool-note' | 'tool-pencil' | 'tool-pointer' | 'tool-text' | 'trash' | 'triangle-down' | 'triangle-up' | 'twitter' | 'undo' | 'ungroup' | 'unlock-small' | 'unlock' | 'vertical-align-center' | 'vertical-align-end' | 'vertical-align-start' | 'visible' | 'warning-triangle' | 'zoom-in' | 'zoom-out';
|
||||
export type TLUiIconType = 'align-bottom-center' | 'align-bottom-left' | 'align-bottom-right' | 'align-bottom' | 'align-center-center' | 'align-center-horizontal' | 'align-center-left' | 'align-center-right' | 'align-center-vertical' | 'align-left' | 'align-right' | 'align-top-center' | 'align-top-left' | 'align-top-right' | 'align-top' | 'arrow-left' | 'arrowhead-arrow' | 'arrowhead-bar' | 'arrowhead-diamond' | 'arrowhead-dot' | 'arrowhead-none' | 'arrowhead-square' | 'arrowhead-triangle-inverted' | 'arrowhead-triangle' | 'aspect-ratio' | 'avatar' | 'blob' | 'bring-forward' | 'bring-to-front' | 'check' | 'checkbox-checked' | 'checkbox-empty' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'chevrons-ne' | 'chevrons-sw' | 'clipboard-copied' | 'clipboard-copy' | 'code' | 'collab' | 'color' | 'comment' | 'cross-2' | 'cross' | 'dash-dashed' | 'dash-dotted' | 'dash-draw' | 'dash-solid' | 'discord' | 'distribute-horizontal' | 'distribute-vertical' | 'dot' | 'dots-horizontal' | 'dots-vertical' | 'drag-handle-dots' | 'duplicate' | 'edit' | 'external-link' | 'file' | 'fill-none' | 'fill-pattern' | 'fill-semi' | 'fill-solid' | 'follow' | 'following' | 'font-draw' | 'font-mono' | 'font-sans' | 'font-serif' | 'geo-arrow-down' | 'geo-arrow-left' | 'geo-arrow-right' | 'geo-arrow-up' | 'geo-check-box' | 'geo-diamond' | 'geo-ellipse' | 'geo-hexagon' | 'geo-octagon' | 'geo-oval' | 'geo-pentagon' | 'geo-rectangle' | 'geo-rhombus-2' | 'geo-rhombus' | 'geo-star' | 'geo-trapezoid' | 'geo-triangle' | 'geo-x-box' | 'github' | 'group' | 'hidden' | 'image' | 'info-circle' | 'leading' | 'link' | 'lock-small' | 'lock' | 'menu' | 'minus' | 'mixed' | 'pack' | 'page' | 'plus' | 'question-mark-circle' | 'question-mark' | 'redo' | 'reset-zoom' | 'rotate-ccw' | 'rotate-cw' | 'ruler' | 'search' | 'send-backward' | 'send-to-back' | 'settings-horizontal' | 'settings-vertical-1' | 'settings-vertical' | 'share-1' | 'share-2' | 'size-extra-large' | 'size-large' | 'size-medium' | 'size-small' | 'spline-cubic' | 'spline-line' | 'stack-horizontal' | 'stack-vertical' | 'stretch-horizontal' | 'stretch-vertical' | 'text-align-center' | 'text-align-justify' | 'text-align-left' | 'text-align-right' | 'tool-arrow' | 'tool-embed' | 'tool-eraser' | 'tool-frame' | 'tool-hand' | 'tool-highlight' | 'tool-laser' | 'tool-line' | 'tool-media' | 'tool-note' | 'tool-pencil' | 'tool-pointer' | 'tool-text' | 'trash' | 'triangle-down' | 'triangle-up' | 'twitter' | 'undo' | 'ungroup' | 'unlock-small' | 'unlock' | 'vertical-align-center' | 'vertical-align-end' | 'vertical-align-start' | 'visible' | 'warning-triangle' | 'zoom-in' | 'zoom-out';
|
||||
|
||||
// @public (undocumented)
|
||||
export const TLUiIconTypes: readonly ["align-bottom-center", "align-bottom-left", "align-bottom-right", "align-bottom", "align-center-center", "align-center-horizontal", "align-center-left", "align-center-right", "align-center-vertical", "align-left", "align-right", "align-top-center", "align-top-left", "align-top-right", "align-top", "arrow-left", "arrowhead-arrow", "arrowhead-bar", "arrowhead-diamond", "arrowhead-dot", "arrowhead-none", "arrowhead-square", "arrowhead-triangle-inverted", "arrowhead-triangle", "aspect-ratio", "avatar", "blob", "bring-forward", "bring-to-front", "check", "checkbox-checked", "checkbox-empty", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "chevrons-ne", "chevrons-sw", "clipboard-copied", "clipboard-copy", "code", "collab", "color", "comment", "cross-2", "cross", "dash-dashed", "dash-dotted", "dash-draw", "dash-solid", "discord", "distribute-horizontal", "distribute-vertical", "dot", "dots-horizontal", "dots-vertical", "drag-handle-dots", "duplicate", "edit", "external-link", "file", "fill-none", "fill-pattern", "fill-semi", "fill-solid", "follow", "following", "font-draw", "font-mono", "font-sans", "font-serif", "geo-arrow-down", "geo-arrow-left", "geo-arrow-right", "geo-arrow-up", "geo-check-box", "geo-diamond", "geo-ellipse", "geo-hexagon", "geo-octagon", "geo-oval", "geo-pentagon", "geo-rectangle", "geo-rhombus-2", "geo-rhombus", "geo-star", "geo-trapezoid", "geo-triangle", "geo-x-box", "github", "group", "hidden", "image", "info-circle", "leading", "link", "lock-small", "lock", "menu", "minus", "mixed", "pack", "page", "plus", "question-mark-circle", "question-mark", "redo", "reset-zoom", "rotate-ccw", "rotate-cw", "ruler", "search", "send-backward", "send-to-back", "settings-horizontal", "settings-vertical-1", "settings-vertical", "share-1", "share-2", "size-extra-large", "size-large", "size-medium", "size-small", "spline-cubic", "spline-line", "stack-horizontal", "stack-vertical", "stretch-horizontal", "stretch-vertical", "text-align-center", "text-align-justify", "text-align-left", "text-align-right", "tool-arrow", "tool-embed", "tool-eraser", "tool-frame", "tool-hand", "tool-highlight", "tool-highlighter", "tool-laser", "tool-line", "tool-media", "tool-note", "tool-pencil", "tool-pointer", "tool-text", "trash", "triangle-down", "triangle-up", "twitter", "undo", "ungroup", "unlock-small", "unlock", "vertical-align-center", "vertical-align-end", "vertical-align-start", "visible", "warning-triangle", "zoom-in", "zoom-out"];
|
||||
export const TLUiIconTypes: readonly ["align-bottom-center", "align-bottom-left", "align-bottom-right", "align-bottom", "align-center-center", "align-center-horizontal", "align-center-left", "align-center-right", "align-center-vertical", "align-left", "align-right", "align-top-center", "align-top-left", "align-top-right", "align-top", "arrow-left", "arrowhead-arrow", "arrowhead-bar", "arrowhead-diamond", "arrowhead-dot", "arrowhead-none", "arrowhead-square", "arrowhead-triangle-inverted", "arrowhead-triangle", "aspect-ratio", "avatar", "blob", "bring-forward", "bring-to-front", "check", "checkbox-checked", "checkbox-empty", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "chevrons-ne", "chevrons-sw", "clipboard-copied", "clipboard-copy", "code", "collab", "color", "comment", "cross-2", "cross", "dash-dashed", "dash-dotted", "dash-draw", "dash-solid", "discord", "distribute-horizontal", "distribute-vertical", "dot", "dots-horizontal", "dots-vertical", "drag-handle-dots", "duplicate", "edit", "external-link", "file", "fill-none", "fill-pattern", "fill-semi", "fill-solid", "follow", "following", "font-draw", "font-mono", "font-sans", "font-serif", "geo-arrow-down", "geo-arrow-left", "geo-arrow-right", "geo-arrow-up", "geo-check-box", "geo-diamond", "geo-ellipse", "geo-hexagon", "geo-octagon", "geo-oval", "geo-pentagon", "geo-rectangle", "geo-rhombus-2", "geo-rhombus", "geo-star", "geo-trapezoid", "geo-triangle", "geo-x-box", "github", "group", "hidden", "image", "info-circle", "leading", "link", "lock-small", "lock", "menu", "minus", "mixed", "pack", "page", "plus", "question-mark-circle", "question-mark", "redo", "reset-zoom", "rotate-ccw", "rotate-cw", "ruler", "search", "send-backward", "send-to-back", "settings-horizontal", "settings-vertical-1", "settings-vertical", "share-1", "share-2", "size-extra-large", "size-large", "size-medium", "size-small", "spline-cubic", "spline-line", "stack-horizontal", "stack-vertical", "stretch-horizontal", "stretch-vertical", "text-align-center", "text-align-justify", "text-align-left", "text-align-right", "tool-arrow", "tool-embed", "tool-eraser", "tool-frame", "tool-hand", "tool-highlight", "tool-laser", "tool-line", "tool-media", "tool-note", "tool-pencil", "tool-pointer", "tool-text", "trash", "triangle-down", "triangle-up", "twitter", "undo", "ungroup", "unlock-small", "unlock", "vertical-align-center", "vertical-align-end", "vertical-align-start", "visible", "warning-triangle", "zoom-in", "zoom-out"];
|
||||
|
||||
// @public (undocumented)
|
||||
export const ToastsContext: Context<ToastsContextType>;
|
||||
|
|
|
@ -181,6 +181,7 @@ const DebugMenuContent = track(function DebugMenuContent({
|
|||
<DropdownMenu.Group>
|
||||
<Toggle label="Read-only" value={app.isReadOnly} onChange={(r) => app.setReadOnly(r)} />
|
||||
<DebugFlagToggle flag={debugFlags.debugSvg} />
|
||||
<DebugFlagToggle flag={debugFlags.forceSrgb} />
|
||||
<DebugFlagToggle
|
||||
flag={debugFlags.debugCursors}
|
||||
onChange={(enabled) => {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { App, featureFlags, useApp } from '@tldraw/editor'
|
||||
import { compact } from '@tldraw/utils'
|
||||
import React from 'react'
|
||||
import { useValue } from 'signia-react'
|
||||
import { ToolItem, ToolsContextType, useTools } from './useTools'
|
||||
|
@ -45,7 +46,7 @@ export function ToolbarSchemaProvider({ overrides, children }: ToolbarSchemaProv
|
|||
const highlighterEnabled = useValue(featureFlags.highlighterTool)
|
||||
|
||||
const toolbarSchema = React.useMemo<ToolbarSchemaContextType>(() => {
|
||||
const schema: ToolbarSchemaContextType = [
|
||||
const schema: ToolbarSchemaContextType = compact([
|
||||
toolbarItem(tools.select),
|
||||
toolbarItem(tools.hand),
|
||||
toolbarItem(tools.draw),
|
||||
|
@ -62,23 +63,20 @@ export function ToolbarSchemaProvider({ overrides, children }: ToolbarSchemaProv
|
|||
toolbarItem(tools['rhombus']),
|
||||
toolbarItem(tools['pentagon']),
|
||||
toolbarItem(tools['hexagon']),
|
||||
toolbarItem(tools['octagon']),
|
||||
// toolbarItem(tools['octagon']),
|
||||
toolbarItem(tools['star']),
|
||||
toolbarItem(tools['oval']),
|
||||
toolbarItem(tools.line),
|
||||
toolbarItem(tools['arrow-right']),
|
||||
toolbarItem(tools['x-box']),
|
||||
toolbarItem(tools['check-box']),
|
||||
toolbarItem(tools['arrow-left']),
|
||||
toolbarItem(tools['arrow-up']),
|
||||
toolbarItem(tools['arrow-down']),
|
||||
toolbarItem(tools['x-box']),
|
||||
toolbarItem(tools['check-box']),
|
||||
toolbarItem(tools['arrow-right']),
|
||||
toolbarItem(tools.frame),
|
||||
toolbarItem(tools.line),
|
||||
highlighterEnabled ? toolbarItem(tools.highlight) : null,
|
||||
toolbarItem(tools.laser),
|
||||
]
|
||||
|
||||
if (highlighterEnabled) {
|
||||
schema.push(toolbarItem(tools.highlight))
|
||||
}
|
||||
])
|
||||
|
||||
if (overrides) {
|
||||
return overrides(app, schema, { tools })
|
||||
|
|
|
@ -210,7 +210,7 @@ export function ToolsProvider({ overrides, children }: ToolsProviderProps) {
|
|||
readonlyOk: true,
|
||||
icon: 'tool-highlight',
|
||||
// TODO: pick a better shortcut
|
||||
kbd: 'i',
|
||||
kbd: '!d',
|
||||
onSelect(source) {
|
||||
app.setSelectedTool('highlight')
|
||||
trackEvent('select-tool', { source, id: 'highlight' })
|
||||
|
|
|
@ -142,7 +142,6 @@ export type TLUiIconType =
|
|||
| 'tool-frame'
|
||||
| 'tool-hand'
|
||||
| 'tool-highlight'
|
||||
| 'tool-highlighter'
|
||||
| 'tool-laser'
|
||||
| 'tool-line'
|
||||
| 'tool-media'
|
||||
|
@ -307,7 +306,6 @@ export const TLUiIconTypes = [
|
|||
'tool-frame',
|
||||
'tool-hand',
|
||||
'tool-highlight',
|
||||
'tool-highlighter',
|
||||
'tool-laser',
|
||||
'tool-line',
|
||||
'tool-media',
|
||||
|
|
Loading…
Reference in a new issue