2023-06-17 22:33:21 +00:00
|
|
|
/* @tldraw/editor */
|
[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>
2023-06-01 15:34:59 +00:00
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-container {
|
2023-04-25 11:01:25 +00:00
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
font-size: 12px;
|
|
|
|
/* Spacing */
|
|
|
|
--space-1: 2px;
|
|
|
|
--space-2: 4px;
|
|
|
|
--space-3: 8px;
|
|
|
|
--space-4: 12px;
|
|
|
|
--space-5: 16px;
|
|
|
|
--space-6: 20px;
|
|
|
|
--space-7: 28px;
|
|
|
|
--space-8: 32px;
|
|
|
|
--space-9: 64px;
|
|
|
|
--space-10: 72px;
|
|
|
|
/* Radius */
|
|
|
|
--radius-0: 2px;
|
|
|
|
--radius-1: 4px;
|
|
|
|
--radius-2: 7px;
|
|
|
|
--radius-3: 9px;
|
|
|
|
--radius-4: 12px;
|
|
|
|
--radius-5: 16px;
|
|
|
|
--layer-grid: 150;
|
|
|
|
--layer-canvas: 200;
|
|
|
|
/* Misc */
|
2023-05-05 14:14:42 +00:00
|
|
|
--tl-zoom: 1;
|
2023-05-23 14:12:11 +00:00
|
|
|
|
2023-06-05 14:49:44 +00:00
|
|
|
/* Cursor SVGs */
|
2023-05-23 14:12:11 +00:00
|
|
|
--tl-cursor-none: none;
|
2023-06-05 14:49:44 +00:00
|
|
|
--tl-cursor-default: url("data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m12 24.4219v-16.015l11.591 11.619h-6.781l-.411.124z' fill='white'/><path d='m21.0845 25.0962-3.605 1.535-4.682-11.089 3.686-1.553z' fill='white'/><path d='m19.751 24.4155-1.844.774-3.1-7.374 1.841-.775z' fill='black'/><path d='m13 10.814v11.188l2.969-2.866.428-.139h4.768z' fill='black'/></g></svg>")
|
|
|
|
12 8,
|
2023-06-24 08:24:10 +00:00
|
|
|
default;
|
2023-06-05 14:49:44 +00:00
|
|
|
--tl-cursor-pointer: url("data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m13.3315 21.3799c-.284-.359-.629-1.093-1.243-1.984-.348-.504-1.211-1.453-1.468-1.935-.223-.426-.199-.617-.146-.97.094-.628.738-1.117 1.425-1.051.519.049.959.392 1.355.716.239.195.533.574.71.788.163.196.203.277.377.509.23.307.302.459.214.121-.071-.496-.187-1.343-.355-2.092-.128-.568-.159-.657-.281-1.093-.129-.464-.195-.789-.316-1.281-.084-.348-.235-1.059-.276-1.459-.057-.547-.087-1.439.264-1.849.275-.321.906-.418 1.297-.22.512.259.803 1.003.936 1.3.239.534.387 1.151.516 1.961.164 1.031.466 2.462.476 2.763.024-.369-.068-1.146-.004-1.5.058-.321.328-.694.666-.795.286-.085.621-.116.916-.055.313.064.643.288.766.499.362.624.369 1.899.384 1.831.086-.376.071-1.229.284-1.584.14-.234.497-.445.687-.479.294-.052.655-.068.964-.008.249.049.586.345.677.487.218.344.342 1.317.379 1.658.015.141.074-.392.293-.736.406-.639 1.843-.763 1.898.639.025.654.02.624.02 1.064 0 .517-.012.828-.04 1.202-.031.4-.117 1.304-.242 1.742-.086.301-.371.978-.652 1.384 0 0-1.074 1.25-1.191 1.813-.118.562-.079.566-.102.965-.023.398.121.922.121.922s-.802.104-1.234.035c-.391-.063-.875-.841-1-1.079-.172-.328-.539-.265-.682-.023-.225.383-.709 1.07-1.051 1.113-.668.084-2.054.031-3.139.02 0 0 .185-1.011-.227-1.358-.305-.259-.83-.784-1.144-1.06z' fill='white'/><g stroke='black' stroke-linecap='round' stroke-width='.75'><path d='m13.3315 21.3799c-.284-.359-.629-1.093-1.243-1.984-.348-.504-1.211-1.453-1.468-1.935-.223-.426-.199-.617-.146-.97.094-.628.738-1.117 1.425-1.051.519.049.959.392 1.355.716.239.195.533.574.71.788.163.196.203.277.377.509.23.307.302.459.214.121-.071-.496-.187-1.343-.355-2.092-.128-.568-.159-.657-.281-1.093-.129-.464-.195-.789-.316-1.281-.084-.348-.235-1.059-.276-1.459-.057-.547-.087-1.439.264-1.849.275-.321.906-.418 1.297-.22.512.259.803 1.003.936 1.3.239.534.387 1.151.516 1.961.164 1.031.466 2.462.476 2.763.024-.369-.068-1.146-.004-1.5.058-.321.328-.694.666-.795.286-.085.621-.116.916-.055.313.064.643.288.766.499.362.624.369 1.899.384 1.831.086-.376.071-1.229.284-1.584.14-.234.497-.445.687-.479.294-.052.655-.068.964-.008.249.049.586.345.677.487.218.344.342 1.317.379 1.658.015.141.074-.392.293-.736.406-.639 1.843-.763 1.898.639.025.654.02.624.02 1.064 0 .517-.012.828-.04 1.202-.031.4-.117 1.304-.242 1.742-.086.301-.371.978-.652 1.384 0 0-1.074 1.25-1.191 1.813-.118.562-.079.566-.102.965-.023.398.121.922.121.922s-.802.104-1.234.035c-.391-.063-.875-.841-1-1.079-.172-.328-.539-.265-.682-.023-.225.383-.709 1.07-1.051 1.113-.668.084-2.054.031-3.139.02 0 0 .185-1.011-.227-1.358-.305-.259-.83-.784-1.144-1.06z' stroke-linejoin='round'/><path d='m21.5664 21.7344v-3.459'/><path d='m19.5508 21.7461-.016-3.473'/><path d='m17.5547 18.3047.021 3.426'/></g></g></svg>")
|
|
|
|
14 10,
|
|
|
|
pointer;
|
|
|
|
--tl-cursor-cross: url("data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m25 16h-6.01v-6h-2.98v6h-6.01v3h6.01v6h2.98v-6h6.01z' fill='white'/><path d='m23.9902 17.0103h-6v-6.01h-.98v6.01h-6v.98h6v6.01h.98v-6.01h6z' fill='%23231f1f'/></g></svg>")
|
|
|
|
16 16,
|
2023-06-24 08:24:10 +00:00
|
|
|
crosshair;
|
2023-06-05 14:49:44 +00:00
|
|
|
--tl-cursor-move: url("data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m19 14h1v1h-1zm1 6h-1v-1h1zm-5-5h-1v-1h1zm0 5h-1v-1h1zm2-10.987-7.985 7.988 5.222 5.221 2.763 2.763 7.984-7.985z' fill='white'/><g fill='black'><path d='m23.5664 16.9971-2.557-2.809v1.829h-4.009-4.001v-1.829l-2.571 2.809 2.572 2.808-.001-1.808h4.001 4.009l-.001 1.808z'/><path d='m17.9873 17h.013v-4.001l1.807.001-2.807-2.571-2.809 2.57h1.809v4.001h.008v4.002l-1.828-.001 2.807 2.577 2.805-2.576h-1.805z'/></g></g></svg>")
|
|
|
|
16 16,
|
2023-06-24 08:24:10 +00:00
|
|
|
move;
|
2023-06-05 14:49:44 +00:00
|
|
|
--tl-cursor-grab: url("data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m13.5557 17.5742c-.098-.375-.196-.847-.406-1.552-.167-.557-.342-.859-.47-1.233-.155-.455-.303-.721-.496-1.181-.139-.329-.364-1.048-.457-1.44-.119-.509.033-.924.244-1.206.253-.339.962-.49 1.357-.351.371.13.744.512.916.788.288.46.357.632.717 1.542.393.992.564 1.918.611 2.231l.085.452c-.001-.04-.043-1.122-.044-1.162-.035-1.029-.06-1.823-.038-2.939.002-.126.064-.587.084-.715.078-.5.305-.8.673-.979.412-.201.926-.215 1.401-.017.423.173.626.55.687 1.022.014.109.094.987.093 1.107-.013 1.025.006 1.641.015 2.174.004.231.003 1.625.017 1.469.061-.656.094-3.189.344-3.942.144-.433.405-.746.794-.929.431-.203 1.113-.07 1.404.243.285.305.446.692.482 1.153.032.405-.019.897-.02 1.245 0 .867-.021 1.324-.037 2.121-.001.038-.015.298.023.182.094-.28.188-.542.266-.745.049-.125.241-.614.359-.859.114-.234.211-.369.415-.688.2-.313.415-.448.668-.561.54-.235 1.109.112 1.301.591.086.215.009.713-.028 1.105-.061.647-.254 1.306-.352 1.648-.128.447-.274 1.235-.34 1.601-.072.394-.234 1.382-.359 1.82-.086.301-.371.978-.652 1.384 0 0-1.074 1.25-1.192 1.812-.117.563-.078.567-.101.965-.024.399.121.923.121.923s-.802.104-1.234.034c-.391-.062-.875-.841-1-1.078-.172-.328-.539-.265-.682-.023-.225.383-.709 1.07-1.051 1.113-.668.084-2.054.03-3.139.02 0 0 .185-1.011-.227-1.358-.305-.26-.83-.784-1.144-1.06l-.832-.921c-.284-.36-.629-1.093-1.243-1.985-.348-.504-1.027-1.085-1.284-1.579-.223-.425-.331-.954-.19-1.325.225-.594.675-.897 1.362-.832.519.05.848.206 1.238.537.225.19.573.534.75.748.163.195.203.276.377.509.23.307.302.459.214.121' fill='white'/><g stroke='black' stroke-linecap='round' stroke-width='.75'><path d='m13.5557 17.5742c-.098-.375-.196-.847-.406-1.552-.167-.557-.342-.859-.47-1.233-.155-.455-.303-.721-.496-1.181-.139-.329-.364-1.048-.457-1.44-.119-.509.033-.924.244-1.206.253-.339.962-.49 1.357-.351.371.13.744.512.916.788.288.46.357.632.717 1.542.393.992.564 1.918.611 2.231l.085.452c-.001-.04-.043-1.122-.044-1.162-.035-1.029-.06-1.823-.038-2.939.002-.126.064-.587.084-.715.078-.5.305-.8.673-.979.412-.201.926-.215 1.401-.017.423.173.626.55.687 1.022.014.109.094.987.093 1.107-.013 1.025.006 1.641.015 2.174.004.231.003 1.625.017 1.469.061-.656.094-3.189.344-3.942.144-.433.405-.746.794-.929.431-.203 1.113-.07 1.404.243.285.305.446.692.482 1.153.032.405-.019.897-.02 1.245 0 .867-.021 1.324-.037 2.121-.001.038-.015.298.023.182.094-.28.188-.542.266-.745.049-.125.241-.614.359-.859.114-.234.211-.369.415-.688.2-.313.415-.448.668-.561.54-.235 1.109.112 1.301.591.086.215.009.713-.028 1.105-.061.647-.254 1.306-.352 1.648-.128.447-.274 1.235-.34 1.601-.072.394-.234 1.382-.359 1.82-.086.301-.371.978-.652 1.384 0 0-1.074 1.25-1.192 1.812-.117.563-.078.567-.101.965-.024.399.121.923.121.923s-.802.104-1.234.034c-.391-.062-.875-.841-1-1.078-.172-.328-.539-.265-.682-.023-.225.383-.709 1.07-1.051 1.113-.668.084-2.054.03-3.139.02 0 0 .185-1.011-.227-1.358-.305-.26-.83-.784-1.144-1.06l-.832-.921c-.284-.36-.629-1.093-1.243-1.985-.348-.504-1.027-1.085-1.284-1.579-.223-.425-.331-.954-.19-1.325.225-.594.675-.897 1.362-.832.519.05.848.206 1.238.537.225.19.573.534.75.748.163.195.203.276.377.509.23.307.302.459.214.121' stroke-linejoin='round'/><path d='m20.5664 21.7344v-3.459'/><path d='m18.5508 21.7461-.016-3.473'/><path d='m16.5547 18.3047.021 3.426'/></g></g></svg>")
|
|
|
|
16 16,
|
2023-06-24 08:24:10 +00:00
|
|
|
grab;
|
2023-06-05 14:49:44 +00:00
|
|
|
--tl-cursor-grabbing: url("data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m13.5732 12.0361c.48-.178 1.427-.069 1.677.473.213.462.396 1.241.406 1.075.024-.369-.024-1.167.137-1.584.117-.304.347-.59.686-.691.285-.086.62-.116.916-.055.313.064.642.287.765.499.362.623.368 1.899.385 1.831.064-.272.07-1.229.283-1.584.141-.235.497-.445.687-.479.294-.052.656-.068.964-.008.249.049.586.344.677.487.219.344.342 1.316.379 1.658.016.141.074-.393.293-.736.406-.639 1.844-.763 1.898.639.026.654.02.624.02 1.064 0 .516-.012.828-.04 1.202-.03.399-.116 1.304-.241 1.742-.086.301-.371.978-.653 1.384 0 0-1.074 1.25-1.191 1.812-.117.563-.078.567-.102.965-.023.399.121.923.121.923s-.801.104-1.234.034c-.391-.062-.875-.84-1-1.078-.172-.328-.539-.265-.682-.023-.224.383-.709 1.07-1.05 1.113-.669.084-2.055.03-3.14.02 0 0 .185-1.011-.227-1.358-.305-.26-.83-.784-1.144-1.06l-.832-.921c-.283-.36-1.002-.929-1.243-1.985-.213-.936-.192-1.395.037-1.77.232-.381.67-.589.854-.625.208-.042.692-.039.875.062.223.123.313.159.488.391.23.307.312.456.213.121-.076-.262-.322-.595-.434-.97-.109-.361-.401-.943-.38-1.526.008-.221.103-.771.832-1.042' fill='white'/><g stroke='black' stroke-width='.75'><path d='m13.5732 12.0361c.48-.178 1.427-.069 1.677.473.213.462.396 1.241.406 1.075.024-.369-.024-1.167.137-1.584.117-.304.347-.59.686-.691.285-.086.62-.116.916-.055.313.064.642.287.765.499.362.623.368 1.899.385 1.831.064-.272.07-1.229.283-1.584.141-.235.497-.445.687-.479.294-.052.656-.068.964-.008.249.049.586.344.677.487.219.344.342 1.316.379 1.658.016.141.074-.393.293-.736.406-.639 1.844-.763 1.898.639.026.654.02.624.02 1.064 0 .516-.012.828-.04 1.202-.03.399-.116 1.304-.241 1.742-.086.301-.371.978-.653 1.384 0 0-1.074 1.25-1.191 1.812-.117.563-.078.567-.102.965-.023.399.121.923.121.923s-.801.104-1.234.034c-.391-.062-.875-.84-1-1.078-.172-.328-.539-.265-.682-.023-.224.383-.709 1.07-1.05 1.113-.669.084-2.055.03-3.14.02 0 0 .185-1.011-.227-1.358-.305-.26-.83-.784-1.144-1.06l-.832-.921c-.283-.36-1.002-.929-1.243-1.985-.213-.936-.192-1.395.037-1.77.232-.381.67-.589.854-.625.208-.042.692-.039.875.062.223.123.313.159.488.391.23.307.312.456.213.121-.076-.262-.322-.595-.434-.97-.109-.361-.401-.943-.38-1.526.008-.221.103-.771.832-1.042z' stroke-linejoin='round'/><path d='m20.5664 19.7344v-3.459' stroke-linecap='round'/><path d='m18.5508 19.7461-.016-3.473' stroke-linecap='round'/><path d='m16.5547 16.3047.021 3.426' stroke-linecap='round'/></g></g></svg>")
|
|
|
|
16 16,
|
2023-06-24 08:24:10 +00:00
|
|
|
grabbing;
|
2023-06-05 14:49:44 +00:00
|
|
|
--tl-cursor-text: url("data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path fill='white' d='M7.94 0a5.25 5.25 0 0 0-3.47 1.17A5.27 5.27 0 0 0 1 0H0v3h1c1.41 0 1.85.7 2 1v3.94H2v3h1v3c-.13.3-.57 1-2 1H0v3h1a5.27 5.27 0 0 0 3.47-1.17c.98.8 2.21 1.21 3.47 1.17h1v-3h-1c-1.41 0-1.85-.7-2-1v-3H7v-3H6V4c.13-.3.57-1 2-1h1V0H7.94z'/><path fill='black' d='M7.94 2V1a4 4 0 0 0-3.47 1.64A4 4 0 0 0 1 1v1c1.3-.17 2.56.6 3 1.84v5.1H3v1h1v4.16c-.45 1.24-1.7 2-3 1.84v1a4.05 4.05 0 0 0 3.47-1.63 4.05 4.05 0 0 0 3.47 1.63v-1A2.82 2.82 0 0 1 5 14.1V9.93h1v-1H5V3.85A2.81 2.81 0 0 1 7.94 2z'/></g></svg>")
|
|
|
|
4 10,
|
2023-06-24 08:24:10 +00:00
|
|
|
text;
|
2023-06-05 14:49:44 +00:00
|
|
|
--tl-cursor-zoom-in: url("data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m20.5 15c0 3.038-2.462 5.5-5.5 5.5s-5.5-2.462-5.5-5.5 2.462-5.5 5.5-5.5 5.5 2.462 5.5 5.5' fill='white'/><path d='m20.5 15c0 3.038-2.462 5.5-5.5 5.5s-5.5-2.462-5.5-5.5 2.462-5.5 5.5-5.5 5.5 2.462 5.5 5.5z' stroke='black'/><g fill='black'><path d='m18 14h-2v-2h-2v2h-2v1.98h2v2.02h2v-2.02h2z'/><path d='m23.5859 25 1.414-1.414-5.449-5.449-1.414 1.414z'/></g></g></svg>")
|
|
|
|
16 16,
|
2023-06-24 08:24:10 +00:00
|
|
|
zoom-in;
|
2023-06-05 14:49:44 +00:00
|
|
|
--tl-cursor-zoom-out: url("data:image/svg+xml,<svg height='32' width='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' style='color: black;'><defs><filter id='shadow' y='-40%' x='-40%' width='180px' height='180%' color-interpolation-filters='sRGB'><feDropShadow dx='1' dy='1' stdDeviation='1.2' flood-opacity='.5'/></filter></defs><g fill='none' transform='rotate(0 16 16)' filter='url(%23shadow)'><path d='m20.5 15c0 3.038-2.462 5.5-5.5 5.5s-5.5-2.462-5.5-5.5 2.462-5.5 5.5-5.5 5.5 2.462 5.5 5.5' fill='white'/><path d='m20.5 15c0 3.038-2.462 5.5-5.5 5.5s-5.5-2.462-5.5-5.5 2.462-5.5 5.5-5.5 5.5 2.462 5.5 5.5z' stroke='black'/><g fill='black'><path d='m18 16h-5.98v-1.98h5.98z'/><path d='m23.5859 25 1.414-1.414-5.449-5.449-1.414 1.414z'/></g></g></svg>")
|
|
|
|
16 16,
|
2023-06-24 08:24:10 +00:00
|
|
|
zoom-out;
|
2023-05-23 14:12:11 +00:00
|
|
|
|
2023-06-05 14:49:44 +00:00
|
|
|
/* These cursor values get programmatically overridden */
|
|
|
|
/* They're just here to help your editor autocomplete */
|
2023-06-24 08:24:10 +00:00
|
|
|
--tl-cursor: var(--tl-cursor-default);
|
2023-06-15 15:10:08 +00:00
|
|
|
--tl-cursor-resize-edge: ew-resize;
|
|
|
|
--tl-cursor-resize-corner: nesw-resize;
|
|
|
|
--tl-cursor-ew-resize: ew-resize;
|
|
|
|
--tl-cursor-ns-resize: ns-resize;
|
|
|
|
--tl-cursor-nesw-resize: nesw-resize;
|
|
|
|
--tl-cursor-nwse-resize: nwse-resize;
|
|
|
|
--tl-cursor-rotate: pointer;
|
|
|
|
--tl-cursor-nwse-rotate: pointer;
|
|
|
|
--tl-cursor-nesw-rotate: pointer;
|
|
|
|
--tl-cursor-senw-rotate: pointer;
|
|
|
|
--tl-cursor-swne-rotate: pointer;
|
2023-05-05 14:14:42 +00:00
|
|
|
--tl-scale: calc(1 / var(--tl-zoom));
|
|
|
|
--tl-font-draw: 'tldraw_draw', sans-serif;
|
|
|
|
--tl-font-sans: 'tldraw_sans', sans-serif;
|
|
|
|
--tl-font-serif: 'tldraw_serif', serif;
|
|
|
|
--tl-font-mono: 'tldraw_mono', monospace;
|
|
|
|
--a: calc(min(0.5, 1 / var(--tl-zoom)) * 2px);
|
|
|
|
--b: calc(min(0.5, 1 / var(--tl-zoom)) * -2px);
|
|
|
|
--tl-text-outline: 0 var(--b) 0 var(--color-background), 0 var(--a) 0 var(--color-background),
|
2023-04-25 11:01:25 +00:00
|
|
|
var(--b) var(--b) 0 var(--color-background), var(--a) var(--b) 0 var(--color-background),
|
|
|
|
var(--a) var(--a) 0 var(--color-background), var(--b) var(--a) 0 var(--color-background);
|
|
|
|
/* Own properties */
|
|
|
|
position: relative;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
2023-06-20 12:08:49 +00:00
|
|
|
overflow: clip;
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-theme__light {
|
2023-04-25 11:01:25 +00:00
|
|
|
--color-accent: #e64a4a;
|
|
|
|
--color-background: rgb(249, 250, 251);
|
|
|
|
--color-brush-fill: rgba(144, 144, 144, 0.102);
|
|
|
|
--color-brush-stroke: rgba(144, 144, 144, 0.251);
|
`ShapeUtil.getGeometry`, selection rewrite (#1751)
This PR is a significant rewrite of our selection / hit testing logic.
It
- replaces our current geometric helpers (`getBounds`, `getOutline`,
`hitTestPoint`, and `hitTestLineSegment`) with a new geometry API
- moves our hit testing entirely to JS using geometry
- improves selection logic, especially around editing shapes, groups and
frames
- fixes many minor selection bugs (e.g. shapes behind frames)
- removes hit-testing DOM elements from ShapeFill etc.
- adds many new tests around selection
- adds new tests around selection
- makes several superficial changes to surface editor APIs
This PR is hard to evaluate. The `selection-omnibus` test suite is
intended to describe all of the selection behavior, however all existing
tests are also either here preserved and passing or (in a few cases
around editing shapes) are modified to reflect the new behavior.
## Geometry
All `ShapeUtils` implement `getGeometry`, which returns a single
geometry primitive (`Geometry2d`). For example:
```ts
class BoxyShapeUtil {
getGeometry(shape: BoxyShape) {
return new Rectangle2d({
width: shape.props.width,
height: shape.props.height,
isFilled: true,
margin: shape.props.strokeWidth
})
}
}
```
This geometric primitive is used for all bounds calculation, hit
testing, intersection with arrows, etc.
There are several geometric primitives that extend `Geometry2d`:
- `Arc2d`
- `Circle2d`
- `CubicBezier2d`
- `CubicSpline2d`
- `Edge2d`
- `Ellipse2d`
- `Group2d`
- `Polygon2d`
- `Rectangle2d`
- `Stadium2d`
For shapes that have more complicated geometric representations, such as
an arrow with a label, the `Group2d` can accept other primitives as its
children.
## Hit testing
Previously, we did all hit testing via events set on shapes and other
elements. In this PR, I've replaced those hit tests with our own
calculation for hit tests in JavaScript. This removed the need for many
DOM elements, such as hit test area borders and fills which only existed
to trigger pointer events.
## Selection
We now support selecting "hollow" shapes by clicking inside of them.
This involves a lot of new logic but it should work intuitively. See
`Editor.getShapeAtPoint` for the (thoroughly commented) implementation.
![Kapture 2023-07-23 at 23 27
27](https://github.com/tldraw/tldraw/assets/23072548/a743275c-acdb-42d9-a3fe-b3e20dce86b6)
every sunset is actually the sun hiding in fear and respect of tldraw's
quality of interactions
This PR also fixes several bugs with scribble selection, in particular
around the shift key modifier.
![Kapture 2023-07-24 at 23 34
07](https://github.com/tldraw/tldraw/assets/23072548/871d67d0-8d06-42ae-a2b2-021effba37c5)
...as well as issues with labels and editing.
There are **over 100 new tests** for selection covering groups, frames,
brushing, scribbling, hovering, and editing. I'll add a few more before
I feel comfortable merging this PR.
## Arrow binding
Using the same "hollow shape" logic as selection, arrow binding is
significantly improved.
![Kapture 2023-07-22 at 07 46
25](https://github.com/tldraw/tldraw/assets/23072548/5aa724b3-b57d-4fb7-92d0-80e34246753c)
a thousand wise men could not improve on this
## Moving focus between editing shapes
Previously, this was handled in the `editing_shapes` state. This is
moved to `useEditableText`, and should generally be considered an
advanced implementation detail on a shape-by-shape basis. This addresses
a bug that I'd never noticed before, but which can be reproduced by
selecting an shape—but not focusing its input—while editing a different
shape. Previously, the new shape became the editing shape but its input
did not focus.
![Kapture 2023-07-23 at 23 19
09](https://github.com/tldraw/tldraw/assets/23072548/a5e157fb-24a8-42bd-a692-04ce769b1a9c)
In this PR, you can select a shape by clicking on its edge or body, or
select its input to transfer editing / focus.
![Kapture 2023-07-23 at 23 22
21](https://github.com/tldraw/tldraw/assets/23072548/7384e7ea-9777-4e1a-8f63-15de2166a53a)
tldraw, glorious tldraw
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Erase shapes
2. Select shapes
3. Calculate their bounding boxes
- [ ] Unit Tests // todo
- [ ] End to end tests // todo
### Release Notes
- [editor] Remove `ShapeUtil.getBounds`, `ShapeUtil.getOutline`,
`ShapeUtil.hitTestPoint`, `ShapeUtil.hitTestLineSegment`
- [editor] Add `ShapeUtil.getGeometry`
- [editor] Add `Editor.getShapeGeometry`
2023-07-25 16:10:15 +00:00
|
|
|
--color-grid: rgb(109, 109, 109);
|
2023-04-25 11:01:25 +00:00
|
|
|
--color-low: rgb(237, 240, 242);
|
|
|
|
--color-culled: rgb(235, 238, 240);
|
|
|
|
--color-muted-0: rgba(0, 0, 0, 0.02);
|
|
|
|
--color-muted-1: rgba(0, 0, 0, 0.1);
|
|
|
|
--color-muted-2: rgba(0, 0, 0, 0.035);
|
|
|
|
--color-hint: rgba(0, 0, 0, 0.055);
|
|
|
|
--color-overlay: rgba(0, 0, 0, 0.2);
|
|
|
|
--color-divider: #e8e8e8;
|
|
|
|
--color-panel-contrast: #ffffff;
|
|
|
|
--color-panel-overlay: rgba(255, 255, 255, 0.82);
|
|
|
|
--color-panel: #fdfdfd;
|
|
|
|
--color-focus: #004094;
|
|
|
|
--color-selected: #2f80ed;
|
|
|
|
--color-selected-contrast: #ffffff;
|
|
|
|
--color-selection-fill: #1e90ff06;
|
|
|
|
--color-selection-stroke: #2f80ed;
|
|
|
|
--color-text-0: #1d1d1d;
|
|
|
|
--color-text-1: #2d2d2d;
|
|
|
|
--color-text-2: #5f6369;
|
|
|
|
--color-text-3: #b6b7ba;
|
|
|
|
--color-primary: #2f80ed;
|
|
|
|
--color-warn: #d10b0b;
|
|
|
|
--color-text: #000000;
|
2023-05-19 11:09:13 +00:00
|
|
|
--color-laser: #ff0000;
|
[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>
2023-06-01 15:34:59 +00:00
|
|
|
|
2023-04-25 11:01:25 +00:00
|
|
|
--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);
|
|
|
|
--shadow-3: 0px 1px 2px rgba(0, 0, 0, 0.25), 0px 2px 6px rgba(0, 0, 0, 0.14),
|
|
|
|
inset 0px 0px 0px 1px var(--color-panel-contrast);
|
|
|
|
--shadow-4: 0px 0px 3px rgba(0, 0, 0, 0.16), 0px 5px 4px rgba(0, 0, 0, 0.16),
|
|
|
|
0px 2px 16px rgba(0, 0, 0, 0.06), inset 0px 0px 0px 1px var(--color-panel-contrast);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-theme__dark {
|
2023-04-25 11:01:25 +00:00
|
|
|
--color-accent: #e64a4a;
|
|
|
|
--color-background: #212529;
|
|
|
|
--color-brush-fill: rgba(180, 180, 180, 0.05);
|
|
|
|
--color-brush-stroke: rgba(180, 180, 180, 0.25);
|
`ShapeUtil.getGeometry`, selection rewrite (#1751)
This PR is a significant rewrite of our selection / hit testing logic.
It
- replaces our current geometric helpers (`getBounds`, `getOutline`,
`hitTestPoint`, and `hitTestLineSegment`) with a new geometry API
- moves our hit testing entirely to JS using geometry
- improves selection logic, especially around editing shapes, groups and
frames
- fixes many minor selection bugs (e.g. shapes behind frames)
- removes hit-testing DOM elements from ShapeFill etc.
- adds many new tests around selection
- adds new tests around selection
- makes several superficial changes to surface editor APIs
This PR is hard to evaluate. The `selection-omnibus` test suite is
intended to describe all of the selection behavior, however all existing
tests are also either here preserved and passing or (in a few cases
around editing shapes) are modified to reflect the new behavior.
## Geometry
All `ShapeUtils` implement `getGeometry`, which returns a single
geometry primitive (`Geometry2d`). For example:
```ts
class BoxyShapeUtil {
getGeometry(shape: BoxyShape) {
return new Rectangle2d({
width: shape.props.width,
height: shape.props.height,
isFilled: true,
margin: shape.props.strokeWidth
})
}
}
```
This geometric primitive is used for all bounds calculation, hit
testing, intersection with arrows, etc.
There are several geometric primitives that extend `Geometry2d`:
- `Arc2d`
- `Circle2d`
- `CubicBezier2d`
- `CubicSpline2d`
- `Edge2d`
- `Ellipse2d`
- `Group2d`
- `Polygon2d`
- `Rectangle2d`
- `Stadium2d`
For shapes that have more complicated geometric representations, such as
an arrow with a label, the `Group2d` can accept other primitives as its
children.
## Hit testing
Previously, we did all hit testing via events set on shapes and other
elements. In this PR, I've replaced those hit tests with our own
calculation for hit tests in JavaScript. This removed the need for many
DOM elements, such as hit test area borders and fills which only existed
to trigger pointer events.
## Selection
We now support selecting "hollow" shapes by clicking inside of them.
This involves a lot of new logic but it should work intuitively. See
`Editor.getShapeAtPoint` for the (thoroughly commented) implementation.
![Kapture 2023-07-23 at 23 27
27](https://github.com/tldraw/tldraw/assets/23072548/a743275c-acdb-42d9-a3fe-b3e20dce86b6)
every sunset is actually the sun hiding in fear and respect of tldraw's
quality of interactions
This PR also fixes several bugs with scribble selection, in particular
around the shift key modifier.
![Kapture 2023-07-24 at 23 34
07](https://github.com/tldraw/tldraw/assets/23072548/871d67d0-8d06-42ae-a2b2-021effba37c5)
...as well as issues with labels and editing.
There are **over 100 new tests** for selection covering groups, frames,
brushing, scribbling, hovering, and editing. I'll add a few more before
I feel comfortable merging this PR.
## Arrow binding
Using the same "hollow shape" logic as selection, arrow binding is
significantly improved.
![Kapture 2023-07-22 at 07 46
25](https://github.com/tldraw/tldraw/assets/23072548/5aa724b3-b57d-4fb7-92d0-80e34246753c)
a thousand wise men could not improve on this
## Moving focus between editing shapes
Previously, this was handled in the `editing_shapes` state. This is
moved to `useEditableText`, and should generally be considered an
advanced implementation detail on a shape-by-shape basis. This addresses
a bug that I'd never noticed before, but which can be reproduced by
selecting an shape—but not focusing its input—while editing a different
shape. Previously, the new shape became the editing shape but its input
did not focus.
![Kapture 2023-07-23 at 23 19
09](https://github.com/tldraw/tldraw/assets/23072548/a5e157fb-24a8-42bd-a692-04ce769b1a9c)
In this PR, you can select a shape by clicking on its edge or body, or
select its input to transfer editing / focus.
![Kapture 2023-07-23 at 23 22
21](https://github.com/tldraw/tldraw/assets/23072548/7384e7ea-9777-4e1a-8f63-15de2166a53a)
tldraw, glorious tldraw
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Erase shapes
2. Select shapes
3. Calculate their bounding boxes
- [ ] Unit Tests // todo
- [ ] End to end tests // todo
### Release Notes
- [editor] Remove `ShapeUtil.getBounds`, `ShapeUtil.getOutline`,
`ShapeUtil.hitTestPoint`, `ShapeUtil.hitTestLineSegment`
- [editor] Add `ShapeUtil.getGeometry`
- [editor] Add `Editor.getShapeGeometry`
2023-07-25 16:10:15 +00:00
|
|
|
--color-grid: #909090;
|
2023-04-25 11:01:25 +00:00
|
|
|
--color-low: #2c3136;
|
|
|
|
--color-culled: rgb(47, 52, 57);
|
|
|
|
--color-muted-0: rgba(255, 255, 255, 0.02);
|
|
|
|
--color-muted-1: rgba(255, 255, 255, 0.1);
|
|
|
|
--color-muted-2: rgba(255, 255, 255, 0.05);
|
|
|
|
--color-hint: rgba(255, 255, 255, 0.1);
|
|
|
|
--color-overlay: rgba(0, 0, 0, 0.35);
|
|
|
|
--color-divider: #49555f;
|
|
|
|
--color-panel-contrast: #49555f;
|
|
|
|
--color-panel: #363d44;
|
|
|
|
--color-panel-overlay: rgba(54, 61, 68, 0.82);
|
|
|
|
--color-focus: #a5c3f3;
|
|
|
|
--color-selected: #4285f4;
|
|
|
|
--color-selected-contrast: #ffffff;
|
|
|
|
--color-selection-fill: rgba(38, 150, 255, 0.05);
|
|
|
|
--color-selection-stroke: #2f80ed;
|
|
|
|
--color-text-0: #f0eded;
|
|
|
|
--color-text-1: #d9d9d9;
|
|
|
|
--color-text-2: #8e9094;
|
|
|
|
--color-text-3: #515a62;
|
|
|
|
--color-primary: #2f80ed;
|
|
|
|
--color-warn: #d10b0b;
|
|
|
|
--color-text: #f8f9fa;
|
2023-05-19 11:09:13 +00:00
|
|
|
--color-laser: #ff0000;
|
[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>
2023-06-01 15:34:59 +00:00
|
|
|
|
2023-04-25 11:01:25 +00:00
|
|
|
--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,
|
|
|
|
inset 0px 0px 0px 1px var(--color-panel-contrast);
|
|
|
|
--shadow-3: 0px 1px 3px #00000077, 0px 2px 12px rgba(0, 0, 0, 0.22),
|
|
|
|
inset 0px 0px 0px 1px var(--color-panel-contrast);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-counter-scaled {
|
|
|
|
transform: scale(var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
transform-origin: top left;
|
2023-05-05 14:14:42 +00:00
|
|
|
width: calc(100% * var(--tl-zoom));
|
|
|
|
height: calc(100% * var(--tl-zoom));
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-container,
|
|
|
|
.tl-container * {
|
2023-04-25 11:01:25 +00:00
|
|
|
-webkit-touch-callout: none;
|
|
|
|
-webkit-tap-highlight-color: transparent;
|
|
|
|
scrollbar-highlight-color: transparent;
|
|
|
|
-webkit-user-select: none;
|
|
|
|
user-select: none;
|
2023-06-29 14:23:18 +00:00
|
|
|
box-sizing: border-box;
|
2023-04-25 11:01:25 +00:00
|
|
|
outline: none;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-container a {
|
2023-04-25 11:01:25 +00:00
|
|
|
-webkit-touch-callout: initial;
|
|
|
|
}
|
|
|
|
|
2023-07-19 10:52:21 +00:00
|
|
|
.tl-container:focus-within {
|
|
|
|
outline: 1px solid var(--color-low);
|
|
|
|
}
|
|
|
|
|
2023-04-25 11:01:25 +00:00
|
|
|
input,
|
|
|
|
*[contenteditable],
|
|
|
|
*[contenteditable] * {
|
|
|
|
-webkit-user-select: text;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------- */
|
|
|
|
/* Canvas */
|
|
|
|
/* -------------------------------------------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-canvas {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
background-color: var(--color-background);
|
|
|
|
color: var(--color-text);
|
|
|
|
z-index: var(--layer-canvas);
|
2023-05-05 14:14:42 +00:00
|
|
|
cursor: var(--tl-cursor);
|
2023-04-25 11:01:25 +00:00
|
|
|
overflow: clip;
|
|
|
|
content-visibility: auto;
|
|
|
|
touch-action: none;
|
|
|
|
contain: strict;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-shapes {
|
|
|
|
position: relative;
|
|
|
|
z-index: 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tl-overlays {
|
|
|
|
position: relative;
|
|
|
|
z-index: 2;
|
|
|
|
}
|
|
|
|
|
2023-05-16 14:35:22 +00:00
|
|
|
.tl-overlays__item {
|
2023-05-05 14:14:42 +00:00
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
`ShapeUtil.getGeometry`, selection rewrite (#1751)
This PR is a significant rewrite of our selection / hit testing logic.
It
- replaces our current geometric helpers (`getBounds`, `getOutline`,
`hitTestPoint`, and `hitTestLineSegment`) with a new geometry API
- moves our hit testing entirely to JS using geometry
- improves selection logic, especially around editing shapes, groups and
frames
- fixes many minor selection bugs (e.g. shapes behind frames)
- removes hit-testing DOM elements from ShapeFill etc.
- adds many new tests around selection
- adds new tests around selection
- makes several superficial changes to surface editor APIs
This PR is hard to evaluate. The `selection-omnibus` test suite is
intended to describe all of the selection behavior, however all existing
tests are also either here preserved and passing or (in a few cases
around editing shapes) are modified to reflect the new behavior.
## Geometry
All `ShapeUtils` implement `getGeometry`, which returns a single
geometry primitive (`Geometry2d`). For example:
```ts
class BoxyShapeUtil {
getGeometry(shape: BoxyShape) {
return new Rectangle2d({
width: shape.props.width,
height: shape.props.height,
isFilled: true,
margin: shape.props.strokeWidth
})
}
}
```
This geometric primitive is used for all bounds calculation, hit
testing, intersection with arrows, etc.
There are several geometric primitives that extend `Geometry2d`:
- `Arc2d`
- `Circle2d`
- `CubicBezier2d`
- `CubicSpline2d`
- `Edge2d`
- `Ellipse2d`
- `Group2d`
- `Polygon2d`
- `Rectangle2d`
- `Stadium2d`
For shapes that have more complicated geometric representations, such as
an arrow with a label, the `Group2d` can accept other primitives as its
children.
## Hit testing
Previously, we did all hit testing via events set on shapes and other
elements. In this PR, I've replaced those hit tests with our own
calculation for hit tests in JavaScript. This removed the need for many
DOM elements, such as hit test area borders and fills which only existed
to trigger pointer events.
## Selection
We now support selecting "hollow" shapes by clicking inside of them.
This involves a lot of new logic but it should work intuitively. See
`Editor.getShapeAtPoint` for the (thoroughly commented) implementation.
![Kapture 2023-07-23 at 23 27
27](https://github.com/tldraw/tldraw/assets/23072548/a743275c-acdb-42d9-a3fe-b3e20dce86b6)
every sunset is actually the sun hiding in fear and respect of tldraw's
quality of interactions
This PR also fixes several bugs with scribble selection, in particular
around the shift key modifier.
![Kapture 2023-07-24 at 23 34
07](https://github.com/tldraw/tldraw/assets/23072548/871d67d0-8d06-42ae-a2b2-021effba37c5)
...as well as issues with labels and editing.
There are **over 100 new tests** for selection covering groups, frames,
brushing, scribbling, hovering, and editing. I'll add a few more before
I feel comfortable merging this PR.
## Arrow binding
Using the same "hollow shape" logic as selection, arrow binding is
significantly improved.
![Kapture 2023-07-22 at 07 46
25](https://github.com/tldraw/tldraw/assets/23072548/5aa724b3-b57d-4fb7-92d0-80e34246753c)
a thousand wise men could not improve on this
## Moving focus between editing shapes
Previously, this was handled in the `editing_shapes` state. This is
moved to `useEditableText`, and should generally be considered an
advanced implementation detail on a shape-by-shape basis. This addresses
a bug that I'd never noticed before, but which can be reproduced by
selecting an shape—but not focusing its input—while editing a different
shape. Previously, the new shape became the editing shape but its input
did not focus.
![Kapture 2023-07-23 at 23 19
09](https://github.com/tldraw/tldraw/assets/23072548/a5e157fb-24a8-42bd-a692-04ce769b1a9c)
In this PR, you can select a shape by clicking on its edge or body, or
select its input to transfer editing / focus.
![Kapture 2023-07-23 at 23 22
21](https://github.com/tldraw/tldraw/assets/23072548/7384e7ea-9777-4e1a-8f63-15de2166a53a)
tldraw, glorious tldraw
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Erase shapes
2. Select shapes
3. Calculate their bounding boxes
- [ ] Unit Tests // todo
- [ ] End to end tests // todo
### Release Notes
- [editor] Remove `ShapeUtil.getBounds`, `ShapeUtil.getOutline`,
`ShapeUtil.hitTestPoint`, `ShapeUtil.hitTestLineSegment`
- [editor] Add `ShapeUtil.getGeometry`
- [editor] Add `Editor.getShapeGeometry`
2023-07-25 16:10:15 +00:00
|
|
|
height: 1px;
|
|
|
|
width: 1px;
|
2023-05-16 14:35:22 +00:00
|
|
|
overflow: visible;
|
2023-05-05 14:14:42 +00:00
|
|
|
pointer-events: none;
|
2023-05-16 14:35:22 +00:00
|
|
|
transform-origin: top left;
|
2023-05-05 14:14:42 +00:00
|
|
|
}
|
|
|
|
|
2023-05-16 14:35:22 +00:00
|
|
|
.tl-svg-context {
|
2023-05-05 14:14:42 +00:00
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
2023-05-16 14:35:22 +00:00
|
|
|
height: 1px;
|
|
|
|
width: 1px;
|
2023-05-05 14:14:42 +00:00
|
|
|
pointer-events: none;
|
|
|
|
}
|
|
|
|
|
tldraw zero - package shuffle (#1710)
This PR moves code between our packages so that:
- @tldraw/editor is a “core” library with the engine and canvas but no
shapes, tools, or other things
- @tldraw/tldraw contains everything particular to the experience we’ve
built for tldraw
At first look, this might seem like a step away from customization and
configuration, however I believe it greatly increases the configuration
potential of the @tldraw/editor while also providing a more accurate
reflection of what configuration options actually exist for
@tldraw/tldraw.
## Library changes
@tldraw/editor re-exports its dependencies and @tldraw/tldraw re-exports
@tldraw/editor.
- users of @tldraw/editor WITHOUT @tldraw/tldraw should almost always
only import things from @tldraw/editor.
- users of @tldraw/tldraw should almost always only import things from
@tldraw/tldraw.
- @tldraw/polyfills is merged into @tldraw/editor
- @tldraw/indices is merged into @tldraw/editor
- @tldraw/primitives is merged mostly into @tldraw/editor, partially
into @tldraw/tldraw
- @tldraw/file-format is merged into @tldraw/tldraw
- @tldraw/ui is merged into @tldraw/tldraw
Many (many) utils and other code is moved from the editor to tldraw. For
example, embeds now are entirely an feature of @tldraw/tldraw. The only
big chunk of code left in core is related to arrow handling.
## API Changes
The editor can now be used without tldraw's assets. We load them in
@tldraw/tldraw instead, so feel free to use whatever fonts or images or
whatever that you like with the editor.
All tools and shapes (except for the `Group` shape) are moved to
@tldraw/tldraw. This includes the `select` tool.
You should use the editor with at least one tool, however, so you now
also need to send in an `initialState` prop to the Editor /
<TldrawEditor> component indicating which state the editor should begin
in.
The `components` prop now also accepts `SelectionForeground`.
The complex selection component that we use for tldraw is moved to
@tldraw/tldraw. The default component is quite basic but can easily be
replaced via the `components` prop. We pass down our tldraw-flavored
SelectionFg via `components`.
Likewise with the `Scribble` component: the `DefaultScribble` no longer
uses our freehand tech and is a simple path instead. We pass down the
tldraw-flavored scribble via `components`.
The `ExternalContentManager` (`Editor.externalContentManager`) is
removed and replaced with a mapping of types to handlers.
- Register new content handlers with
`Editor.registerExternalContentHandler`.
- Register new asset creation handlers (for files and URLs) with
`Editor.registerExternalAssetHandler`
### Change Type
- [x] `major` — Breaking change
### Test Plan
- [x] Unit Tests
- [x] End to end tests
### Release Notes
- [@tldraw/editor] lots, wip
- [@tldraw/ui] gone, merged to tldraw/tldraw
- [@tldraw/polyfills] gone, merged to tldraw/editor
- [@tldraw/primitives] gone, merged to tldraw/editor / tldraw/tldraw
- [@tldraw/indices] gone, merged to tldraw/editor
- [@tldraw/file-format] gone, merged to tldraw/tldraw
---------
Co-authored-by: alex <alex@dytry.ch>
2023-07-17 21:22:34 +00:00
|
|
|
.tl-positioned {
|
2023-05-25 15:41:49 +00:00
|
|
|
position: absolute;
|
tldraw zero - package shuffle (#1710)
This PR moves code between our packages so that:
- @tldraw/editor is a “core” library with the engine and canvas but no
shapes, tools, or other things
- @tldraw/tldraw contains everything particular to the experience we’ve
built for tldraw
At first look, this might seem like a step away from customization and
configuration, however I believe it greatly increases the configuration
potential of the @tldraw/editor while also providing a more accurate
reflection of what configuration options actually exist for
@tldraw/tldraw.
## Library changes
@tldraw/editor re-exports its dependencies and @tldraw/tldraw re-exports
@tldraw/editor.
- users of @tldraw/editor WITHOUT @tldraw/tldraw should almost always
only import things from @tldraw/editor.
- users of @tldraw/tldraw should almost always only import things from
@tldraw/tldraw.
- @tldraw/polyfills is merged into @tldraw/editor
- @tldraw/indices is merged into @tldraw/editor
- @tldraw/primitives is merged mostly into @tldraw/editor, partially
into @tldraw/tldraw
- @tldraw/file-format is merged into @tldraw/tldraw
- @tldraw/ui is merged into @tldraw/tldraw
Many (many) utils and other code is moved from the editor to tldraw. For
example, embeds now are entirely an feature of @tldraw/tldraw. The only
big chunk of code left in core is related to arrow handling.
## API Changes
The editor can now be used without tldraw's assets. We load them in
@tldraw/tldraw instead, so feel free to use whatever fonts or images or
whatever that you like with the editor.
All tools and shapes (except for the `Group` shape) are moved to
@tldraw/tldraw. This includes the `select` tool.
You should use the editor with at least one tool, however, so you now
also need to send in an `initialState` prop to the Editor /
<TldrawEditor> component indicating which state the editor should begin
in.
The `components` prop now also accepts `SelectionForeground`.
The complex selection component that we use for tldraw is moved to
@tldraw/tldraw. The default component is quite basic but can easily be
replaced via the `components` prop. We pass down our tldraw-flavored
SelectionFg via `components`.
Likewise with the `Scribble` component: the `DefaultScribble` no longer
uses our freehand tech and is a simple path instead. We pass down the
tldraw-flavored scribble via `components`.
The `ExternalContentManager` (`Editor.externalContentManager`) is
removed and replaced with a mapping of types to handlers.
- Register new content handlers with
`Editor.registerExternalContentHandler`.
- Register new asset creation handlers (for files and URLs) with
`Editor.registerExternalAssetHandler`
### Change Type
- [x] `major` — Breaking change
### Test Plan
- [x] Unit Tests
- [x] End to end tests
### Release Notes
- [@tldraw/editor] lots, wip
- [@tldraw/ui] gone, merged to tldraw/tldraw
- [@tldraw/polyfills] gone, merged to tldraw/editor
- [@tldraw/primitives] gone, merged to tldraw/editor / tldraw/tldraw
- [@tldraw/indices] gone, merged to tldraw/editor
- [@tldraw/file-format] gone, merged to tldraw/tldraw
---------
Co-authored-by: alex <alex@dytry.ch>
2023-07-17 21:22:34 +00:00
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
transform-origin: top left;
|
2023-05-25 15:41:49 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
/* ------------------- Background ------------------- */
|
|
|
|
|
|
|
|
.tl-background {
|
|
|
|
position: absolute;
|
|
|
|
inset: 0px;
|
|
|
|
background-color: var(--color-background);
|
|
|
|
}
|
|
|
|
|
2023-04-25 11:01:25 +00:00
|
|
|
/* --------------------- Grid Layer --------------------- */
|
|
|
|
|
|
|
|
.tl-grid {
|
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
touch-action: none;
|
|
|
|
pointer-events: none;
|
|
|
|
z-index: 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tl-grid-dot {
|
|
|
|
fill: var(--color-grid);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --------------------- Layers --------------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-html-layer {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
width: 1px;
|
|
|
|
height: 1px;
|
|
|
|
contain: layout style size;
|
|
|
|
z-index: 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------- Brush --------------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-brush {
|
|
|
|
stroke-width: calc(var(--tl-scale) * 1px);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-brush__default {
|
2023-04-25 11:01:25 +00:00
|
|
|
stroke: var(--color-brush-stroke);
|
|
|
|
fill: var(--color-brush-fill);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------- Scribble -------------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-scribble {
|
2023-04-25 11:01:25 +00:00
|
|
|
stroke-linejoin: round;
|
|
|
|
stroke-linecap: round;
|
|
|
|
pointer-events: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------- Shape --------------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-shape {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
pointer-events: none;
|
|
|
|
overflow: visible;
|
|
|
|
transform-origin: top left;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-shape__culled {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: relative;
|
|
|
|
background-color: var(--color-culled);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------- Shape Containers ---------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-svg-container {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
pointer-events: none;
|
|
|
|
stroke-linecap: round;
|
|
|
|
stroke-linejoin: round;
|
|
|
|
transform-origin: top left;
|
|
|
|
overflow: visible;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-html-container {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
pointer-events: none;
|
|
|
|
stroke-linecap: round;
|
|
|
|
stroke-linejoin: round;
|
|
|
|
/* content-visibility: auto; */
|
|
|
|
transform-origin: top left;
|
|
|
|
color: inherit;
|
|
|
|
}
|
|
|
|
|
2023-05-16 14:35:22 +00:00
|
|
|
/* --------------- Overlay Stack --------------- */
|
|
|
|
|
|
|
|
/* back of the stack, behind user's stuff */
|
|
|
|
.tl-collaborator__scribble {
|
|
|
|
z-index: 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tl-collaborator__brush {
|
2023-05-19 11:16:03 +00:00
|
|
|
z-index: 20;
|
2023-05-16 14:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.tl-collaborator__shape-indicator {
|
2023-05-19 11:16:03 +00:00
|
|
|
z-index: 30;
|
2023-05-16 14:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.tl-user-scribble {
|
2023-05-19 11:16:03 +00:00
|
|
|
z-index: 40;
|
2023-05-16 14:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.tl-user-brush {
|
2023-05-19 11:16:03 +00:00
|
|
|
z-index: 50;
|
2023-05-16 14:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.tl-user-indicator__selected {
|
2023-05-19 11:16:03 +00:00
|
|
|
z-index: 60;
|
2023-05-16 14:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.tl-user-indicator__hovered {
|
2023-05-19 11:16:03 +00:00
|
|
|
z-index: 70;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tl-user-handles {
|
|
|
|
z-index: 80;
|
2023-05-16 14:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.tl-user-snapline {
|
2023-05-19 11:16:03 +00:00
|
|
|
z-index: 90;
|
2023-05-16 14:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.tl-selection__fg {
|
|
|
|
pointer-events: none;
|
2023-05-19 11:16:03 +00:00
|
|
|
z-index: 100;
|
2023-05-16 14:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.tl-user-indicator__hint {
|
2023-05-19 11:16:03 +00:00
|
|
|
z-index: 110;
|
2023-05-16 14:35:22 +00:00
|
|
|
stroke-width: calc(2.5px * var(--tl-scale));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* behind collaborator cursor */
|
|
|
|
.tl-collaborator__cursor-hint {
|
2023-05-19 11:16:03 +00:00
|
|
|
z-index: 120;
|
2023-05-16 14:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.tl-collaborator__cursor {
|
2023-05-19 11:16:03 +00:00
|
|
|
z-index: 130;
|
2023-05-16 14:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.tl-cursor {
|
|
|
|
overflow: visible;
|
|
|
|
}
|
|
|
|
|
2023-04-25 11:01:25 +00:00
|
|
|
/* -------------------- Indicator ------------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-shape-indicator {
|
2023-04-25 11:01:25 +00:00
|
|
|
transform-origin: top left;
|
|
|
|
fill: none;
|
2023-05-05 14:14:42 +00:00
|
|
|
stroke-width: calc(1.5px * var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------ SelectionBox ------------------ */
|
|
|
|
|
2023-05-16 14:35:22 +00:00
|
|
|
.tl-selection__bg {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
transform-origin: top left;
|
|
|
|
background-color: transparent;
|
|
|
|
pointer-events: all;
|
|
|
|
}
|
|
|
|
|
2023-05-16 14:35:22 +00:00
|
|
|
.tl-selection__fg__outline {
|
2023-04-25 11:01:25 +00:00
|
|
|
fill: none;
|
|
|
|
pointer-events: none;
|
|
|
|
stroke: var(--color-selection-stroke);
|
2023-05-05 14:14:42 +00:00
|
|
|
stroke-width: calc(1.5px * var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-corner-handle {
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: none;
|
|
|
|
stroke: var(--color-selection-stroke);
|
|
|
|
fill: var(--color-background);
|
2023-05-05 14:14:42 +00:00
|
|
|
stroke-width: calc(1.5px * var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-handle {
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: none;
|
|
|
|
fill: var(--color-selection-stroke);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-corner-crop-handle {
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: none;
|
|
|
|
fill: none;
|
|
|
|
stroke: var(--color-selection-stroke);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-corner-crop-edge-handle {
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: none;
|
|
|
|
fill: none;
|
|
|
|
stroke: var(--color-selection-stroke);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-rotate-handle {
|
2023-04-25 11:01:25 +00:00
|
|
|
stroke: var(--color-selection-stroke);
|
|
|
|
fill: var(--color-background);
|
2023-05-05 14:14:42 +00:00
|
|
|
stroke-width: calc(1.5px * var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: all;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-mobile-rotate__bg {
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: all;
|
2023-05-23 14:12:11 +00:00
|
|
|
cursor: var(--tl-cursor-grab);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-mobile-rotate__fg {
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: none;
|
|
|
|
stroke: var(--color-selection-stroke);
|
|
|
|
fill: var(--color-background);
|
2023-05-05 14:14:42 +00:00
|
|
|
stroke-width: calc(1.5px * var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-transparent {
|
2023-04-25 11:01:25 +00:00
|
|
|
fill: transparent;
|
|
|
|
stroke: transparent;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --------------------- Handles -------------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-handle {
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: all;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-handle__bg {
|
2023-04-25 11:01:25 +00:00
|
|
|
fill: transparent;
|
|
|
|
stroke: transparent;
|
|
|
|
pointer-events: all;
|
2023-05-23 14:12:11 +00:00
|
|
|
cursor: var(--tl-cursor-grabbing);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-handle__fg {
|
2023-04-25 11:01:25 +00:00
|
|
|
fill: var(--color-background);
|
|
|
|
stroke: var(--color-selection-stroke);
|
2023-05-05 14:14:42 +00:00
|
|
|
stroke-width: calc(1.5px * var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: none;
|
|
|
|
}
|
|
|
|
|
2023-07-22 05:19:16 +00:00
|
|
|
.tl-handle__create {
|
|
|
|
opacity: 0;
|
|
|
|
}
|
|
|
|
.tl-handle__create:hover {
|
|
|
|
opacity: 1;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-handle__bg:active {
|
2023-04-25 11:01:25 +00:00
|
|
|
fill: none;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-handle__bg:hover {
|
2023-05-23 14:12:11 +00:00
|
|
|
cursor: var(--tl-cursor-grab);
|
2023-04-25 11:01:25 +00:00
|
|
|
fill: var(--color-selection-fill);
|
|
|
|
}
|
|
|
|
|
|
|
|
@media (pointer: coarse) {
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-handle__bg:active {
|
2023-04-25 11:01:25 +00:00
|
|
|
fill: var(--color-selection-fill);
|
|
|
|
}
|
|
|
|
|
2023-07-22 05:19:16 +00:00
|
|
|
.tl-handle__create {
|
2023-04-25 11:01:25 +00:00
|
|
|
opacity: 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------ Bounds Detail ----------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-image,
|
|
|
|
.tl-video {
|
2023-04-25 11:01:25 +00:00
|
|
|
object-fit: cover;
|
|
|
|
background-size: cover;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-image-container,
|
2023-08-25 16:22:52 +00:00
|
|
|
.tl-video-container,
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-embed-container {
|
2023-04-25 11:01:25 +00:00
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
pointer-events: all;
|
2023-08-25 16:22:52 +00:00
|
|
|
/* background-color: var(--color-background); */
|
|
|
|
|
2023-04-25 11:01:25 +00:00
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-image__tg {
|
|
|
|
--scale: calc(min(2, var(--tl-scale)));
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
top: calc(var(--scale) * 8px);
|
|
|
|
right: calc(var(--scale) * 8px);
|
|
|
|
font-size: 10px;
|
|
|
|
transform-origin: top right;
|
|
|
|
background-color: var(--color-background);
|
|
|
|
padding: 2px 4px;
|
|
|
|
border-radius: 4px;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
/* --------------------- Nametag -------------------- */
|
2023-04-25 11:01:25 +00:00
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-collaborator-cursor {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-nametag {
|
|
|
|
position: absolute;
|
|
|
|
top: 16px;
|
|
|
|
left: 13px;
|
|
|
|
width: fit-content;
|
|
|
|
height: fit-content;
|
|
|
|
max-width: 120px;
|
2023-06-15 15:10:08 +00:00
|
|
|
padding: 3px 6px;
|
|
|
|
white-space: nowrap;
|
|
|
|
position: absolute;
|
|
|
|
overflow: hidden;
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
font-size: 12px;
|
2023-06-20 12:08:49 +00:00
|
|
|
font-family: var(--font-body);
|
2023-06-15 15:10:08 +00:00
|
|
|
border-radius: var(--radius-2);
|
2023-04-25 11:01:25 +00:00
|
|
|
color: var(--color-selected-contrast);
|
2023-06-15 15:10:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.tl-nametag-title {
|
|
|
|
position: absolute;
|
|
|
|
top: -2px;
|
|
|
|
left: 13px;
|
|
|
|
width: fit-content;
|
|
|
|
height: fit-content;
|
|
|
|
padding: 0px 6px;
|
|
|
|
max-width: 120px;
|
2023-04-25 11:01:25 +00:00
|
|
|
white-space: nowrap;
|
|
|
|
position: absolute;
|
2023-05-05 14:14:42 +00:00
|
|
|
overflow: hidden;
|
|
|
|
text-overflow: ellipsis;
|
2023-04-25 11:01:25 +00:00
|
|
|
font-size: 12px;
|
2023-06-20 12:08:49 +00:00
|
|
|
font-family: var(--font-body);
|
2023-06-15 15:10:08 +00:00
|
|
|
text-shadow: var(--tl-text-outline);
|
|
|
|
color: var(--color-selected-contrast);
|
|
|
|
}
|
|
|
|
|
|
|
|
.tl-nametag-chat {
|
|
|
|
position: absolute;
|
|
|
|
top: 16px;
|
|
|
|
left: 13px;
|
|
|
|
width: fit-content;
|
|
|
|
height: fit-content;
|
|
|
|
color: var(--color-selected-contrast);
|
|
|
|
white-space: nowrap;
|
|
|
|
position: absolute;
|
|
|
|
padding: 3px 6px;
|
|
|
|
font-size: 12px;
|
2023-06-20 12:08:49 +00:00
|
|
|
font-family: var(--font-body);
|
2023-06-15 15:10:08 +00:00
|
|
|
opacity: 1;
|
|
|
|
border-radius: var(--radius-2);
|
|
|
|
}
|
|
|
|
|
|
|
|
.tl-cursor-chat {
|
|
|
|
position: absolute;
|
|
|
|
color: var(--color-selected-contrast);
|
|
|
|
white-space: nowrap;
|
|
|
|
padding: 3px 6px;
|
|
|
|
font-size: 12px;
|
2023-06-20 12:08:49 +00:00
|
|
|
font-family: var(--font-body);
|
2023-06-15 15:10:08 +00:00
|
|
|
pointer-events: none;
|
|
|
|
z-index: var(--layer-cursor);
|
|
|
|
margin-top: 16px;
|
|
|
|
margin-left: 13px;
|
|
|
|
opacity: 1;
|
|
|
|
border: none;
|
|
|
|
user-select: text;
|
|
|
|
border-radius: var(--radius-2);
|
|
|
|
}
|
|
|
|
|
2023-06-20 12:08:49 +00:00
|
|
|
.tl-cursor-chat .tl-cursor-chat__bubble {
|
|
|
|
padding-right: 12px;
|
|
|
|
}
|
|
|
|
|
2023-06-15 15:10:08 +00:00
|
|
|
.tl-cursor-chat::selection {
|
|
|
|
background: var(--color-selected);
|
|
|
|
color: var(--color-selected-contrast);
|
|
|
|
text-shadow: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tl-cursor-chat-fade {
|
|
|
|
/* Setting to zero causes it to immediately disappear */
|
|
|
|
/* Setting to near-zero causes it to fade out gradually */
|
|
|
|
opacity: 0.0001;
|
|
|
|
transition: opacity 5s ease-in-out;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tl-cursor-chat::placeholder {
|
|
|
|
color: var(--color-selected-contrast);
|
|
|
|
opacity: 0.7;
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------- */
|
|
|
|
/* Spinner */
|
|
|
|
/* -------------------------------------------------- */
|
|
|
|
|
|
|
|
@keyframes spinner {
|
|
|
|
to {
|
|
|
|
transform: rotate(360deg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-spinner::after {
|
2023-04-25 11:01:25 +00:00
|
|
|
content: '';
|
|
|
|
box-sizing: border-box;
|
|
|
|
position: absolute;
|
|
|
|
top: 50%;
|
|
|
|
left: 50%;
|
|
|
|
width: 20px;
|
|
|
|
height: 20px;
|
|
|
|
margin-top: -10px;
|
|
|
|
margin-left: -10px;
|
|
|
|
border-radius: 50%;
|
|
|
|
border: 2px solid #ccc;
|
|
|
|
border-top-color: #000;
|
|
|
|
animation: spinner 0.6s linear infinite;
|
|
|
|
pointer-events: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------- IconShape ------------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-iconshape__icon {
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: all;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-icon-preview {
|
2023-04-25 11:01:25 +00:00
|
|
|
width: 14px;
|
|
|
|
height: 14px;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------- Text Shape ------------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-shape__wrapper {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: relative;
|
|
|
|
font-weight: normal;
|
|
|
|
min-width: 1px;
|
|
|
|
padding: 0px;
|
|
|
|
margin: 0px;
|
|
|
|
border: none;
|
|
|
|
height: 100%;
|
|
|
|
font-variant: normal;
|
|
|
|
font-style: normal;
|
|
|
|
pointer-events: all;
|
|
|
|
white-space: pre-wrap;
|
|
|
|
overflow-wrap: break-word;
|
2023-05-05 14:14:42 +00:00
|
|
|
text-shadow: var(--tl-text-outline);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
`ShapeUtil.getGeometry`, selection rewrite (#1751)
This PR is a significant rewrite of our selection / hit testing logic.
It
- replaces our current geometric helpers (`getBounds`, `getOutline`,
`hitTestPoint`, and `hitTestLineSegment`) with a new geometry API
- moves our hit testing entirely to JS using geometry
- improves selection logic, especially around editing shapes, groups and
frames
- fixes many minor selection bugs (e.g. shapes behind frames)
- removes hit-testing DOM elements from ShapeFill etc.
- adds many new tests around selection
- adds new tests around selection
- makes several superficial changes to surface editor APIs
This PR is hard to evaluate. The `selection-omnibus` test suite is
intended to describe all of the selection behavior, however all existing
tests are also either here preserved and passing or (in a few cases
around editing shapes) are modified to reflect the new behavior.
## Geometry
All `ShapeUtils` implement `getGeometry`, which returns a single
geometry primitive (`Geometry2d`). For example:
```ts
class BoxyShapeUtil {
getGeometry(shape: BoxyShape) {
return new Rectangle2d({
width: shape.props.width,
height: shape.props.height,
isFilled: true,
margin: shape.props.strokeWidth
})
}
}
```
This geometric primitive is used for all bounds calculation, hit
testing, intersection with arrows, etc.
There are several geometric primitives that extend `Geometry2d`:
- `Arc2d`
- `Circle2d`
- `CubicBezier2d`
- `CubicSpline2d`
- `Edge2d`
- `Ellipse2d`
- `Group2d`
- `Polygon2d`
- `Rectangle2d`
- `Stadium2d`
For shapes that have more complicated geometric representations, such as
an arrow with a label, the `Group2d` can accept other primitives as its
children.
## Hit testing
Previously, we did all hit testing via events set on shapes and other
elements. In this PR, I've replaced those hit tests with our own
calculation for hit tests in JavaScript. This removed the need for many
DOM elements, such as hit test area borders and fills which only existed
to trigger pointer events.
## Selection
We now support selecting "hollow" shapes by clicking inside of them.
This involves a lot of new logic but it should work intuitively. See
`Editor.getShapeAtPoint` for the (thoroughly commented) implementation.
![Kapture 2023-07-23 at 23 27
27](https://github.com/tldraw/tldraw/assets/23072548/a743275c-acdb-42d9-a3fe-b3e20dce86b6)
every sunset is actually the sun hiding in fear and respect of tldraw's
quality of interactions
This PR also fixes several bugs with scribble selection, in particular
around the shift key modifier.
![Kapture 2023-07-24 at 23 34
07](https://github.com/tldraw/tldraw/assets/23072548/871d67d0-8d06-42ae-a2b2-021effba37c5)
...as well as issues with labels and editing.
There are **over 100 new tests** for selection covering groups, frames,
brushing, scribbling, hovering, and editing. I'll add a few more before
I feel comfortable merging this PR.
## Arrow binding
Using the same "hollow shape" logic as selection, arrow binding is
significantly improved.
![Kapture 2023-07-22 at 07 46
25](https://github.com/tldraw/tldraw/assets/23072548/5aa724b3-b57d-4fb7-92d0-80e34246753c)
a thousand wise men could not improve on this
## Moving focus between editing shapes
Previously, this was handled in the `editing_shapes` state. This is
moved to `useEditableText`, and should generally be considered an
advanced implementation detail on a shape-by-shape basis. This addresses
a bug that I'd never noticed before, but which can be reproduced by
selecting an shape—but not focusing its input—while editing a different
shape. Previously, the new shape became the editing shape but its input
did not focus.
![Kapture 2023-07-23 at 23 19
09](https://github.com/tldraw/tldraw/assets/23072548/a5e157fb-24a8-42bd-a692-04ce769b1a9c)
In this PR, you can select a shape by clicking on its edge or body, or
select its input to transfer editing / focus.
![Kapture 2023-07-23 at 23 22
21](https://github.com/tldraw/tldraw/assets/23072548/7384e7ea-9777-4e1a-8f63-15de2166a53a)
tldraw, glorious tldraw
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Erase shapes
2. Select shapes
3. Calculate their bounding boxes
- [ ] Unit Tests // todo
- [ ] End to end tests // todo
### Release Notes
- [editor] Remove `ShapeUtil.getBounds`, `ShapeUtil.getOutline`,
`ShapeUtil.hitTestPoint`, `ShapeUtil.hitTestLineSegment`
- [editor] Add `ShapeUtil.getGeometry`
- [editor] Add `Editor.getShapeGeometry`
2023-07-25 16:10:15 +00:00
|
|
|
.tl-text-shape__wrapper[data-isediting='true'] {
|
|
|
|
outline: calc(var(--tl-scale) * 1.5px) solid var(--color-selected);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-shape__wrapper[data-align='start'] {
|
2023-04-25 11:01:25 +00:00
|
|
|
text-align: left;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-shape__wrapper[data-align='middle'] {
|
2023-04-25 11:01:25 +00:00
|
|
|
text-align: center;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-shape__wrapper[data-align='end'] {
|
2023-04-25 11:01:25 +00:00
|
|
|
text-align: right;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-shape__wrapper[data-font='draw'] {
|
|
|
|
font-family: var(--tl-font-draw);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-shape__wrapper[data-font='sans'] {
|
|
|
|
font-family: var(--tl-font-sans);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-shape__wrapper[data-font='serif'] {
|
|
|
|
font-family: var(--tl-font-serif);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-shape__wrapper[data-font='mono'] {
|
|
|
|
font-family: var(--tl-font-mono);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-shape__wrapper[data-isediting='true'] .tl-text-content {
|
2023-04-25 11:01:25 +00:00
|
|
|
opacity: 0;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text {
|
2023-04-25 11:01:25 +00:00
|
|
|
/* remove overflow from textarea on windows */
|
|
|
|
margin: 0px;
|
|
|
|
padding: 0px;
|
|
|
|
border: 0px;
|
|
|
|
color: inherit;
|
|
|
|
caret-color: var(--color-text);
|
|
|
|
background: none;
|
|
|
|
border-image: none;
|
|
|
|
font-size: inherit;
|
|
|
|
font-family: inherit;
|
|
|
|
font-weight: inherit;
|
|
|
|
line-height: inherit;
|
|
|
|
font-variant: inherit;
|
|
|
|
font-style: inherit;
|
|
|
|
text-align: inherit;
|
|
|
|
letter-spacing: inherit;
|
|
|
|
text-shadow: inherit;
|
|
|
|
outline: none;
|
|
|
|
white-space: pre-wrap;
|
|
|
|
word-wrap: break-word;
|
|
|
|
overflow-wrap: break-word;
|
|
|
|
pointer-events: all;
|
|
|
|
text-rendering: auto;
|
|
|
|
text-transform: none;
|
|
|
|
text-indent: 0px;
|
|
|
|
display: inline-block;
|
|
|
|
appearance: auto;
|
|
|
|
column-count: initial !important;
|
|
|
|
writing-mode: horizontal-tb !important;
|
|
|
|
word-spacing: 0px;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-measure {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
z-index: 999999;
|
|
|
|
top: -9999px;
|
|
|
|
right: -9999px;
|
|
|
|
opacity: 0;
|
|
|
|
width: fit-content;
|
|
|
|
box-sizing: border-box;
|
|
|
|
pointer-events: none;
|
|
|
|
line-break: normal;
|
|
|
|
white-space: pre-wrap;
|
|
|
|
word-wrap: break-word;
|
|
|
|
overflow-wrap: break-word;
|
|
|
|
resize: none;
|
|
|
|
border: none;
|
|
|
|
user-select: none;
|
|
|
|
-webkit-user-select: none;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-edit-container {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: relative;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-input,
|
|
|
|
.tl-text-content {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
min-width: 1px;
|
|
|
|
min-height: 1px;
|
|
|
|
overflow: visible;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-content {
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: none;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-input {
|
2023-04-25 11:01:25 +00:00
|
|
|
resize: none;
|
|
|
|
user-select: all;
|
|
|
|
-webkit-user-select: text;
|
|
|
|
overflow: hidden;
|
2023-05-23 14:12:11 +00:00
|
|
|
cursor: var(--tl-cursor-text);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-input::selection {
|
2023-04-25 11:01:25 +00:00
|
|
|
background: var(--color-selected);
|
|
|
|
color: var(--color-selected-contrast);
|
|
|
|
text-shadow: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------- Snap Lines ------------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-snap-line {
|
2023-04-25 11:01:25 +00:00
|
|
|
stroke: var(--color-accent);
|
2023-05-05 14:14:42 +00:00
|
|
|
stroke-width: calc(1px * var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
fill: none;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-snap-point {
|
2023-04-25 11:01:25 +00:00
|
|
|
stroke: var(--color-accent);
|
2023-05-05 14:14:42 +00:00
|
|
|
stroke-width: calc(1px * var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
fill: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------- Groups ------------------ */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-group {
|
2023-04-25 11:01:25 +00:00
|
|
|
stroke: var(--color-text);
|
2023-05-05 14:14:42 +00:00
|
|
|
stroke-width: calc(1px * var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
opacity: 0.5;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------- Bookmark Shape ------------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-bookmark__container {
|
2023-04-25 11:01:25 +00:00
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
position: relative;
|
|
|
|
border: 1px solid var(--color-panel-contrast);
|
|
|
|
background-color: var(--color-panel);
|
|
|
|
border-radius: var(--radius-2);
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
overflow: hidden;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-bookmark__image_container {
|
2023-04-25 11:01:25 +00:00
|
|
|
flex: 1;
|
|
|
|
overflow: hidden;
|
|
|
|
border-top-left-radius: var(--radius-1);
|
|
|
|
border-top-right-radius: var(--radius-1);
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
display: flex;
|
|
|
|
justify-content: flex-end;
|
|
|
|
align-items: flex-start;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-bookmark__image_container > .tl-hyperlink-button::after {
|
2023-04-25 11:01:25 +00:00
|
|
|
background-color: var(--color-panel);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-bookmark__placeholder {
|
2023-04-25 11:01:25 +00:00
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
background-color: var(--color-muted-2);
|
|
|
|
border-bottom: 1px solid var(--color-muted-2);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-bookmark__image {
|
2023-04-25 11:01:25 +00:00
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
object-fit: cover;
|
|
|
|
object-position: center;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-bookmark__copy_container {
|
2023-04-25 11:01:25 +00:00
|
|
|
background-color: var(--color-muted);
|
|
|
|
padding: var(--space-4);
|
|
|
|
pointer-events: all;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-bookmark__heading,
|
|
|
|
.tl-bookmark__description,
|
|
|
|
.tl-bookmark__link {
|
2023-05-16 08:37:37 +00:00
|
|
|
margin: 0px;
|
2023-04-25 11:01:25 +00:00
|
|
|
width: 100%;
|
|
|
|
font-family: inherit;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-bookmark__heading {
|
2023-04-25 11:01:25 +00:00
|
|
|
font-size: 16px;
|
|
|
|
font-weight: bold;
|
|
|
|
padding-bottom: var(--space-2);
|
`ShapeUtil.getGeometry`, selection rewrite (#1751)
This PR is a significant rewrite of our selection / hit testing logic.
It
- replaces our current geometric helpers (`getBounds`, `getOutline`,
`hitTestPoint`, and `hitTestLineSegment`) with a new geometry API
- moves our hit testing entirely to JS using geometry
- improves selection logic, especially around editing shapes, groups and
frames
- fixes many minor selection bugs (e.g. shapes behind frames)
- removes hit-testing DOM elements from ShapeFill etc.
- adds many new tests around selection
- adds new tests around selection
- makes several superficial changes to surface editor APIs
This PR is hard to evaluate. The `selection-omnibus` test suite is
intended to describe all of the selection behavior, however all existing
tests are also either here preserved and passing or (in a few cases
around editing shapes) are modified to reflect the new behavior.
## Geometry
All `ShapeUtils` implement `getGeometry`, which returns a single
geometry primitive (`Geometry2d`). For example:
```ts
class BoxyShapeUtil {
getGeometry(shape: BoxyShape) {
return new Rectangle2d({
width: shape.props.width,
height: shape.props.height,
isFilled: true,
margin: shape.props.strokeWidth
})
}
}
```
This geometric primitive is used for all bounds calculation, hit
testing, intersection with arrows, etc.
There are several geometric primitives that extend `Geometry2d`:
- `Arc2d`
- `Circle2d`
- `CubicBezier2d`
- `CubicSpline2d`
- `Edge2d`
- `Ellipse2d`
- `Group2d`
- `Polygon2d`
- `Rectangle2d`
- `Stadium2d`
For shapes that have more complicated geometric representations, such as
an arrow with a label, the `Group2d` can accept other primitives as its
children.
## Hit testing
Previously, we did all hit testing via events set on shapes and other
elements. In this PR, I've replaced those hit tests with our own
calculation for hit tests in JavaScript. This removed the need for many
DOM elements, such as hit test area borders and fills which only existed
to trigger pointer events.
## Selection
We now support selecting "hollow" shapes by clicking inside of them.
This involves a lot of new logic but it should work intuitively. See
`Editor.getShapeAtPoint` for the (thoroughly commented) implementation.
![Kapture 2023-07-23 at 23 27
27](https://github.com/tldraw/tldraw/assets/23072548/a743275c-acdb-42d9-a3fe-b3e20dce86b6)
every sunset is actually the sun hiding in fear and respect of tldraw's
quality of interactions
This PR also fixes several bugs with scribble selection, in particular
around the shift key modifier.
![Kapture 2023-07-24 at 23 34
07](https://github.com/tldraw/tldraw/assets/23072548/871d67d0-8d06-42ae-a2b2-021effba37c5)
...as well as issues with labels and editing.
There are **over 100 new tests** for selection covering groups, frames,
brushing, scribbling, hovering, and editing. I'll add a few more before
I feel comfortable merging this PR.
## Arrow binding
Using the same "hollow shape" logic as selection, arrow binding is
significantly improved.
![Kapture 2023-07-22 at 07 46
25](https://github.com/tldraw/tldraw/assets/23072548/5aa724b3-b57d-4fb7-92d0-80e34246753c)
a thousand wise men could not improve on this
## Moving focus between editing shapes
Previously, this was handled in the `editing_shapes` state. This is
moved to `useEditableText`, and should generally be considered an
advanced implementation detail on a shape-by-shape basis. This addresses
a bug that I'd never noticed before, but which can be reproduced by
selecting an shape—but not focusing its input—while editing a different
shape. Previously, the new shape became the editing shape but its input
did not focus.
![Kapture 2023-07-23 at 23 19
09](https://github.com/tldraw/tldraw/assets/23072548/a5e157fb-24a8-42bd-a692-04ce769b1a9c)
In this PR, you can select a shape by clicking on its edge or body, or
select its input to transfer editing / focus.
![Kapture 2023-07-23 at 23 22
21](https://github.com/tldraw/tldraw/assets/23072548/7384e7ea-9777-4e1a-8f63-15de2166a53a)
tldraw, glorious tldraw
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Erase shapes
2. Select shapes
3. Calculate their bounding boxes
- [ ] Unit Tests // todo
- [ ] End to end tests // todo
### Release Notes
- [editor] Remove `ShapeUtil.getBounds`, `ShapeUtil.getOutline`,
`ShapeUtil.hitTestPoint`, `ShapeUtil.hitTestLineSegment`
- [editor] Add `ShapeUtil.getGeometry`
- [editor] Add `Editor.getShapeGeometry`
2023-07-25 16:10:15 +00:00
|
|
|
margin: 8px 0px;
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-bookmark__description {
|
2023-04-25 11:01:25 +00:00
|
|
|
font-size: 12px;
|
|
|
|
padding-bottom: var(--space-4);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-bookmark__link {
|
2023-04-25 11:01:25 +00:00
|
|
|
font-size: 14px;
|
|
|
|
pointer-events: all;
|
|
|
|
z-index: 999;
|
|
|
|
overflow: hidden;
|
|
|
|
display: block;
|
|
|
|
color: var(--color-text);
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
text-decoration: none;
|
|
|
|
color: var(--color-text-2);
|
2023-05-23 14:12:11 +00:00
|
|
|
cursor: var(--tl-cursor-pointer);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-bookmark__link:hover {
|
2023-04-25 11:01:25 +00:00
|
|
|
color: var(--color-selected);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------- Hyperlink Button ---------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-hyperlink-button {
|
2023-04-25 11:01:25 +00:00
|
|
|
background: none;
|
|
|
|
margin: 0px;
|
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
right: 0px;
|
|
|
|
height: 44px;
|
|
|
|
width: 44px;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
z-index: 200;
|
|
|
|
font-size: 12px;
|
|
|
|
font-weight: 400;
|
|
|
|
color: var(--color-text-1);
|
|
|
|
padding: 13px;
|
2023-05-23 14:12:11 +00:00
|
|
|
cursor: var(--tl-cursor-pointer);
|
2023-04-25 11:01:25 +00:00
|
|
|
border: none;
|
|
|
|
outline: none;
|
|
|
|
pointer-events: all;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-hyperlink-button::after {
|
2023-04-25 11:01:25 +00:00
|
|
|
content: '';
|
|
|
|
z-index: -1;
|
|
|
|
position: absolute;
|
|
|
|
right: 6px;
|
|
|
|
bottom: 6px;
|
|
|
|
display: block;
|
|
|
|
width: calc(100% - 12px);
|
|
|
|
height: calc(100% - 12px);
|
|
|
|
border-radius: var(--radius-1);
|
|
|
|
background-color: var(--color-background);
|
|
|
|
pointer-events: none;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-hyperlink-button:hover {
|
2023-04-25 11:01:25 +00:00
|
|
|
color: var(--color-selected);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-hyperlink-button:focus-visible {
|
2023-04-25 11:01:25 +00:00
|
|
|
color: var(--color-selected);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-hyperlink-button__icon {
|
2023-04-25 11:01:25 +00:00
|
|
|
width: 18px;
|
|
|
|
height: 18px;
|
|
|
|
background-color: currentColor;
|
|
|
|
pointer-events: none;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-hyperlink-button__hidden {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
|
2023-04-25 11:01:25 +00:00
|
|
|
/* ---------------- Geo shape ---------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-label {
|
2023-04-25 11:01:25 +00:00
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
2023-05-19 11:19:11 +00:00
|
|
|
align-items: center;
|
2023-04-25 11:01:25 +00:00
|
|
|
color: var(--color-text);
|
2023-05-05 14:14:42 +00:00
|
|
|
text-shadow: var(--tl-text-outline);
|
2023-04-25 11:01:25 +00:00
|
|
|
line-height: inherit;
|
`ShapeUtil.getGeometry`, selection rewrite (#1751)
This PR is a significant rewrite of our selection / hit testing logic.
It
- replaces our current geometric helpers (`getBounds`, `getOutline`,
`hitTestPoint`, and `hitTestLineSegment`) with a new geometry API
- moves our hit testing entirely to JS using geometry
- improves selection logic, especially around editing shapes, groups and
frames
- fixes many minor selection bugs (e.g. shapes behind frames)
- removes hit-testing DOM elements from ShapeFill etc.
- adds many new tests around selection
- adds new tests around selection
- makes several superficial changes to surface editor APIs
This PR is hard to evaluate. The `selection-omnibus` test suite is
intended to describe all of the selection behavior, however all existing
tests are also either here preserved and passing or (in a few cases
around editing shapes) are modified to reflect the new behavior.
## Geometry
All `ShapeUtils` implement `getGeometry`, which returns a single
geometry primitive (`Geometry2d`). For example:
```ts
class BoxyShapeUtil {
getGeometry(shape: BoxyShape) {
return new Rectangle2d({
width: shape.props.width,
height: shape.props.height,
isFilled: true,
margin: shape.props.strokeWidth
})
}
}
```
This geometric primitive is used for all bounds calculation, hit
testing, intersection with arrows, etc.
There are several geometric primitives that extend `Geometry2d`:
- `Arc2d`
- `Circle2d`
- `CubicBezier2d`
- `CubicSpline2d`
- `Edge2d`
- `Ellipse2d`
- `Group2d`
- `Polygon2d`
- `Rectangle2d`
- `Stadium2d`
For shapes that have more complicated geometric representations, such as
an arrow with a label, the `Group2d` can accept other primitives as its
children.
## Hit testing
Previously, we did all hit testing via events set on shapes and other
elements. In this PR, I've replaced those hit tests with our own
calculation for hit tests in JavaScript. This removed the need for many
DOM elements, such as hit test area borders and fills which only existed
to trigger pointer events.
## Selection
We now support selecting "hollow" shapes by clicking inside of them.
This involves a lot of new logic but it should work intuitively. See
`Editor.getShapeAtPoint` for the (thoroughly commented) implementation.
![Kapture 2023-07-23 at 23 27
27](https://github.com/tldraw/tldraw/assets/23072548/a743275c-acdb-42d9-a3fe-b3e20dce86b6)
every sunset is actually the sun hiding in fear and respect of tldraw's
quality of interactions
This PR also fixes several bugs with scribble selection, in particular
around the shift key modifier.
![Kapture 2023-07-24 at 23 34
07](https://github.com/tldraw/tldraw/assets/23072548/871d67d0-8d06-42ae-a2b2-021effba37c5)
...as well as issues with labels and editing.
There are **over 100 new tests** for selection covering groups, frames,
brushing, scribbling, hovering, and editing. I'll add a few more before
I feel comfortable merging this PR.
## Arrow binding
Using the same "hollow shape" logic as selection, arrow binding is
significantly improved.
![Kapture 2023-07-22 at 07 46
25](https://github.com/tldraw/tldraw/assets/23072548/5aa724b3-b57d-4fb7-92d0-80e34246753c)
a thousand wise men could not improve on this
## Moving focus between editing shapes
Previously, this was handled in the `editing_shapes` state. This is
moved to `useEditableText`, and should generally be considered an
advanced implementation detail on a shape-by-shape basis. This addresses
a bug that I'd never noticed before, but which can be reproduced by
selecting an shape—but not focusing its input—while editing a different
shape. Previously, the new shape became the editing shape but its input
did not focus.
![Kapture 2023-07-23 at 23 19
09](https://github.com/tldraw/tldraw/assets/23072548/a5e157fb-24a8-42bd-a692-04ce769b1a9c)
In this PR, you can select a shape by clicking on its edge or body, or
select its input to transfer editing / focus.
![Kapture 2023-07-23 at 23 22
21](https://github.com/tldraw/tldraw/assets/23072548/7384e7ea-9777-4e1a-8f63-15de2166a53a)
tldraw, glorious tldraw
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Erase shapes
2. Select shapes
3. Calculate their bounding boxes
- [ ] Unit Tests // todo
- [ ] End to end tests // todo
### Release Notes
- [editor] Remove `ShapeUtil.getBounds`, `ShapeUtil.getOutline`,
`ShapeUtil.hitTestPoint`, `ShapeUtil.hitTestLineSegment`
- [editor] Add `ShapeUtil.getGeometry`
- [editor] Add `Editor.getShapeGeometry`
2023-07-25 16:10:15 +00:00
|
|
|
position: relative;
|
|
|
|
z-index: 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tl-text-label[data-isediting='true'] {
|
|
|
|
outline: calc(var(--tl-scale) * 1.5px) solid var(--color-selected);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-label[data-isediting='true'] .tl-text-content {
|
2023-04-25 11:01:25 +00:00
|
|
|
opacity: 0;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-label[data-hastext='false'][data-isediting='false'] > .tl-text-label__inner {
|
2023-04-25 11:01:25 +00:00
|
|
|
width: 40px;
|
|
|
|
height: 40px;
|
|
|
|
}
|
|
|
|
|
`ShapeUtil.getGeometry`, selection rewrite (#1751)
This PR is a significant rewrite of our selection / hit testing logic.
It
- replaces our current geometric helpers (`getBounds`, `getOutline`,
`hitTestPoint`, and `hitTestLineSegment`) with a new geometry API
- moves our hit testing entirely to JS using geometry
- improves selection logic, especially around editing shapes, groups and
frames
- fixes many minor selection bugs (e.g. shapes behind frames)
- removes hit-testing DOM elements from ShapeFill etc.
- adds many new tests around selection
- adds new tests around selection
- makes several superficial changes to surface editor APIs
This PR is hard to evaluate. The `selection-omnibus` test suite is
intended to describe all of the selection behavior, however all existing
tests are also either here preserved and passing or (in a few cases
around editing shapes) are modified to reflect the new behavior.
## Geometry
All `ShapeUtils` implement `getGeometry`, which returns a single
geometry primitive (`Geometry2d`). For example:
```ts
class BoxyShapeUtil {
getGeometry(shape: BoxyShape) {
return new Rectangle2d({
width: shape.props.width,
height: shape.props.height,
isFilled: true,
margin: shape.props.strokeWidth
})
}
}
```
This geometric primitive is used for all bounds calculation, hit
testing, intersection with arrows, etc.
There are several geometric primitives that extend `Geometry2d`:
- `Arc2d`
- `Circle2d`
- `CubicBezier2d`
- `CubicSpline2d`
- `Edge2d`
- `Ellipse2d`
- `Group2d`
- `Polygon2d`
- `Rectangle2d`
- `Stadium2d`
For shapes that have more complicated geometric representations, such as
an arrow with a label, the `Group2d` can accept other primitives as its
children.
## Hit testing
Previously, we did all hit testing via events set on shapes and other
elements. In this PR, I've replaced those hit tests with our own
calculation for hit tests in JavaScript. This removed the need for many
DOM elements, such as hit test area borders and fills which only existed
to trigger pointer events.
## Selection
We now support selecting "hollow" shapes by clicking inside of them.
This involves a lot of new logic but it should work intuitively. See
`Editor.getShapeAtPoint` for the (thoroughly commented) implementation.
![Kapture 2023-07-23 at 23 27
27](https://github.com/tldraw/tldraw/assets/23072548/a743275c-acdb-42d9-a3fe-b3e20dce86b6)
every sunset is actually the sun hiding in fear and respect of tldraw's
quality of interactions
This PR also fixes several bugs with scribble selection, in particular
around the shift key modifier.
![Kapture 2023-07-24 at 23 34
07](https://github.com/tldraw/tldraw/assets/23072548/871d67d0-8d06-42ae-a2b2-021effba37c5)
...as well as issues with labels and editing.
There are **over 100 new tests** for selection covering groups, frames,
brushing, scribbling, hovering, and editing. I'll add a few more before
I feel comfortable merging this PR.
## Arrow binding
Using the same "hollow shape" logic as selection, arrow binding is
significantly improved.
![Kapture 2023-07-22 at 07 46
25](https://github.com/tldraw/tldraw/assets/23072548/5aa724b3-b57d-4fb7-92d0-80e34246753c)
a thousand wise men could not improve on this
## Moving focus between editing shapes
Previously, this was handled in the `editing_shapes` state. This is
moved to `useEditableText`, and should generally be considered an
advanced implementation detail on a shape-by-shape basis. This addresses
a bug that I'd never noticed before, but which can be reproduced by
selecting an shape—but not focusing its input—while editing a different
shape. Previously, the new shape became the editing shape but its input
did not focus.
![Kapture 2023-07-23 at 23 19
09](https://github.com/tldraw/tldraw/assets/23072548/a5e157fb-24a8-42bd-a692-04ce769b1a9c)
In this PR, you can select a shape by clicking on its edge or body, or
select its input to transfer editing / focus.
![Kapture 2023-07-23 at 23 22
21](https://github.com/tldraw/tldraw/assets/23072548/7384e7ea-9777-4e1a-8f63-15de2166a53a)
tldraw, glorious tldraw
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Erase shapes
2. Select shapes
3. Calculate their bounding boxes
- [ ] Unit Tests // todo
- [ ] End to end tests // todo
### Release Notes
- [editor] Remove `ShapeUtil.getBounds`, `ShapeUtil.getOutline`,
`ShapeUtil.hitTestPoint`, `ShapeUtil.hitTestLineSegment`
- [editor] Add `ShapeUtil.getGeometry`
- [editor] Add `Editor.getShapeGeometry`
2023-07-25 16:10:15 +00:00
|
|
|
.tl-text-label[data-hastext='true'][data-isediting='false'] .tl-text-content {
|
|
|
|
pointer-events: all;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-label__inner {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: relative;
|
|
|
|
width: fit-content;
|
|
|
|
height: fit-content;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
pointer-events: all;
|
|
|
|
min-height: auto;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-label__inner > .tl-text {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: relative;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
padding: 16px;
|
|
|
|
height: fit-content;
|
|
|
|
width: fit-content;
|
|
|
|
border-radius: var(--radius-1);
|
|
|
|
max-width: 100%;
|
|
|
|
z-index: 3;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-label__inner > .tl-text-input {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
padding: 16px;
|
|
|
|
z-index: 4;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-label[data-textwrap='true'] > .tl-text-label__inner {
|
2023-04-25 11:01:25 +00:00
|
|
|
max-width: 100%;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-label[data-isediting='true'] {
|
2023-08-25 16:22:52 +00:00
|
|
|
background-color: transparent;
|
2023-04-25 11:01:25 +00:00
|
|
|
min-height: auto;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-text-label[data-isediting='true'] p {
|
2023-04-25 11:01:25 +00:00
|
|
|
opacity: 0;
|
|
|
|
}
|
|
|
|
|
2023-05-24 11:34:13 +00:00
|
|
|
.tl-text-label[data-align='start'],
|
|
|
|
.tl-text-label[data-align='start-legacy'] {
|
2023-04-25 11:01:25 +00:00
|
|
|
text-align: left;
|
|
|
|
}
|
|
|
|
|
2023-05-24 11:34:13 +00:00
|
|
|
.tl-text-label[data-align='middle'],
|
|
|
|
.tl-text-label[data-align='middle-legacy'] {
|
2023-04-25 11:01:25 +00:00
|
|
|
text-align: center;
|
|
|
|
}
|
|
|
|
|
2023-05-24 11:34:13 +00:00
|
|
|
.tl-text-label[data-align='end'],
|
|
|
|
.tl-text-label[data-align='end-legacy'] {
|
2023-04-25 11:01:25 +00:00
|
|
|
text-align: right;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-arrow-hint {
|
2023-04-25 11:01:25 +00:00
|
|
|
stroke: var(--color-text-1);
|
|
|
|
fill: none;
|
|
|
|
stroke-linecap: round;
|
|
|
|
overflow: visible;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-arrow-label[data-font='draw'],
|
|
|
|
.tl-text-label[data-font='draw'] {
|
|
|
|
font-family: var(--tl-font-draw);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-arrow-label[data-font='sans'],
|
|
|
|
.tl-text-label[data-font='sans'] {
|
|
|
|
font-family: var(--tl-font-sans);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-arrow-label[data-font='serif'],
|
|
|
|
.tl-text-label[data-font='serif'] {
|
|
|
|
font-family: var(--tl-font-serif);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-arrow-label[data-font='mono'],
|
|
|
|
.tl-text-label[data-font='mono'] {
|
|
|
|
font-family: var(--tl-font-mono);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------- Arrow Shape ------------------ */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-arrow-label {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
top: -1px;
|
|
|
|
left: -1px;
|
|
|
|
width: 2px;
|
|
|
|
height: 2px;
|
|
|
|
padding: 0px;
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
color: var(--color-text);
|
2023-05-05 14:14:42 +00:00
|
|
|
text-shadow: var(--tl-text-outline);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-arrow-label[data-isediting='true'] p {
|
2023-04-25 11:01:25 +00:00
|
|
|
opacity: 0;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-arrow-label[data-isediting='true'] > .tl-arrow-label__inner {
|
2023-04-25 11:01:25 +00:00
|
|
|
background-color: var(--color-background);
|
2023-05-05 14:14:42 +00:00
|
|
|
border: calc(var(--tl-scale) * 1.5px) solid var(--color-selected);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-arrow-label__inner {
|
2023-04-25 11:01:25 +00:00
|
|
|
border-radius: var(--radius-1);
|
|
|
|
box-sizing: content-box;
|
|
|
|
position: relative;
|
|
|
|
height: max-content;
|
|
|
|
width: max-content;
|
|
|
|
pointer-events: all;
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-arrow-label p,
|
|
|
|
.tl-arrow-label textarea {
|
2023-04-25 11:01:25 +00:00
|
|
|
margin: 0px;
|
|
|
|
padding: 0px;
|
|
|
|
border: 0px;
|
|
|
|
color: inherit;
|
|
|
|
caret-color: var(--color-text);
|
|
|
|
background: none;
|
|
|
|
border-image: none;
|
|
|
|
font-size: inherit;
|
|
|
|
font-family: inherit;
|
|
|
|
font-weight: inherit;
|
|
|
|
line-height: inherit;
|
|
|
|
font-variant: inherit;
|
|
|
|
font-style: inherit;
|
|
|
|
text-align: inherit;
|
|
|
|
letter-spacing: inherit;
|
|
|
|
text-shadow: inherit;
|
|
|
|
outline: none;
|
|
|
|
white-space: pre-wrap;
|
|
|
|
word-wrap: break-word;
|
|
|
|
overflow-wrap: break-word;
|
|
|
|
pointer-events: all;
|
|
|
|
text-rendering: auto;
|
|
|
|
text-transform: none;
|
|
|
|
text-indent: 0px;
|
|
|
|
display: inline-block;
|
|
|
|
appearance: auto;
|
|
|
|
column-count: initial !important;
|
|
|
|
writing-mode: horizontal-tb !important;
|
|
|
|
word-spacing: 0px;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-arrow-label p {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: relative;
|
|
|
|
height: max-content;
|
|
|
|
z-index: 2;
|
|
|
|
padding: 4px;
|
|
|
|
overflow: visible;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-arrow-label textarea {
|
2023-04-25 11:01:25 +00:00
|
|
|
z-index: 3;
|
|
|
|
margin: 0px;
|
|
|
|
padding: 4px;
|
|
|
|
height: 100%;
|
|
|
|
width: 100%;
|
|
|
|
position: absolute;
|
|
|
|
resize: none;
|
|
|
|
border: 0px;
|
|
|
|
user-select: all;
|
|
|
|
-webkit-user-select: text;
|
|
|
|
caret-color: var(--color-text);
|
|
|
|
border-image: none;
|
|
|
|
/* Don't allow textarea to be zero width */
|
|
|
|
min-width: 4px;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------- NoteShape ------------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-note__container {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: relative;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
border-radius: var(--radius-2);
|
|
|
|
box-shadow: var(--shadow-1);
|
|
|
|
overflow: hidden;
|
|
|
|
border-color: currentColor;
|
|
|
|
border-style: solid;
|
|
|
|
border-width: 1px;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-note__container .tl-text-label {
|
2023-04-25 11:01:25 +00:00
|
|
|
text-shadow: none;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-note__scrim {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
z-index: 1;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
background-color: var(--color-background);
|
|
|
|
opacity: 0.28;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-loading {
|
2023-06-15 11:57:19 +00:00
|
|
|
background-color: var(--color-background);
|
|
|
|
color: var(--color-text-1);
|
2023-04-25 11:01:25 +00:00
|
|
|
height: 100%;
|
|
|
|
width: 100%;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
gap: var(--space-2);
|
|
|
|
font-size: 14px;
|
|
|
|
font-weight: 500;
|
|
|
|
opacity: 0;
|
|
|
|
animation: fade-in 0.2s ease-in-out forwards;
|
|
|
|
animation-delay: 0.2s;
|
|
|
|
}
|
|
|
|
|
|
|
|
@keyframes fade-in {
|
|
|
|
0% {
|
|
|
|
opacity: 0;
|
|
|
|
}
|
|
|
|
100% {
|
|
|
|
opacity: 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------- FrameShape ------------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-frame__body {
|
|
|
|
stroke-width: calc(1px * var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-frame__hitarea {
|
2023-04-25 11:01:25 +00:00
|
|
|
border-style: solid;
|
2023-05-05 14:14:42 +00:00
|
|
|
border-width: calc(8px * var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
border-color: transparent;
|
|
|
|
background: none;
|
|
|
|
pointer-events: stroke;
|
|
|
|
box-sizing: border-box;
|
2023-05-05 14:14:42 +00:00
|
|
|
top: calc(-8px * var(--tl-scale));
|
|
|
|
left: calc(-8px * var(--tl-scale));
|
|
|
|
width: calc(100% + calc(16px * var(--tl-scale)));
|
|
|
|
height: calc(100% + calc(16px * var(--tl-scale)));
|
2023-04-25 11:01:25 +00:00
|
|
|
z-index: 1;
|
|
|
|
position: absolute;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-frame-heading {
|
2023-04-25 11:01:25 +00:00
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
position: absolute;
|
|
|
|
transform-origin: 0% 100%;
|
|
|
|
overflow: hidden;
|
|
|
|
max-width: 100%;
|
|
|
|
min-width: 32px;
|
|
|
|
height: auto;
|
|
|
|
font-size: 12px;
|
|
|
|
padding-bottom: 4px;
|
|
|
|
pointer-events: all;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-frame-heading-hit-area {
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: all;
|
|
|
|
/* scale from bottom left corner so we can pin it to the top left corner of the frame */
|
|
|
|
transform-origin: 0% 100%;
|
|
|
|
display: flex;
|
|
|
|
height: 100%;
|
|
|
|
width: 100%;
|
|
|
|
align-items: center;
|
|
|
|
border-radius: var(--radius-1);
|
|
|
|
background-color: var(--color-background);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-frame-label {
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: all;
|
|
|
|
overflow: hidden;
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
padding: var(--space-3) var(--space-3);
|
|
|
|
position: relative;
|
|
|
|
font-size: inherit;
|
|
|
|
white-space: pre;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-frame-label__editing {
|
2023-04-25 11:01:25 +00:00
|
|
|
color: transparent;
|
|
|
|
outline: 1.5px solid var(--color-selection-stroke);
|
|
|
|
white-space: pre;
|
|
|
|
width: auto;
|
|
|
|
overflow: visible;
|
|
|
|
background-color: var(--color-panel);
|
|
|
|
border-radius: var(--radius-1);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-frame-name-input {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
border: none;
|
|
|
|
background: none;
|
|
|
|
outline: none;
|
|
|
|
padding: var(--space-3) var(--space-3);
|
|
|
|
inset: 0px;
|
|
|
|
font-size: inherit;
|
|
|
|
font-family: inherit;
|
|
|
|
font-weight: inherit;
|
|
|
|
width: 100%;
|
|
|
|
color: var(--color-text-1);
|
|
|
|
border-radius: var(--radius-1);
|
|
|
|
user-select: all;
|
|
|
|
-webkit-user-select: text;
|
|
|
|
white-space: pre;
|
2023-05-23 14:12:11 +00:00
|
|
|
cursor: var(--tl-cursor-text);
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If mobile use 16px as font size */
|
|
|
|
/* On iOS, font size under 16px in an input will make the page zoom into the input 🤦♂️ */
|
|
|
|
/* https://css-tricks.com/16px-or-larger-text-prevents-ios-form-zoom/ */
|
|
|
|
@media (max-width: 600px) {
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-frame-heading {
|
2023-04-25 11:01:25 +00:00
|
|
|
font-size: 16px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------ iFrames Detail ----------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-embed {
|
2023-04-25 11:01:25 +00:00
|
|
|
border: none;
|
|
|
|
border-radius: var(--radius-2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------- Code Editor ------------------ */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-image__button {
|
2023-04-25 11:01:25 +00:00
|
|
|
padding: 4px 8px;
|
|
|
|
color: var(--color-text);
|
|
|
|
background-color: var(--color-panel);
|
|
|
|
border-radius: var(--radius-2);
|
|
|
|
box-shadow: var(--shadow-1);
|
|
|
|
pointer-events: all;
|
2023-05-23 14:12:11 +00:00
|
|
|
cursor: var(--tl-cursor-pointer);
|
2023-04-25 11:01:25 +00:00
|
|
|
outline: none;
|
|
|
|
display: flex;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-image__button:disabled {
|
2023-04-25 11:01:25 +00:00
|
|
|
opacity: 0.5;
|
|
|
|
pointer-events: none;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-image__toolbox {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
left: 0px;
|
|
|
|
display: flex;
|
|
|
|
justify-content: flex-end;
|
|
|
|
align-items: flex-end;
|
|
|
|
padding: 10px;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-image__toolbox__hidden {
|
2023-04-25 11:01:25 +00:00
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------- Shape Error Boundary -------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-shape-error-boundary {
|
2023-04-25 11:01:25 +00:00
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
background-color: var(--color-muted-1);
|
2023-05-05 14:14:42 +00:00
|
|
|
border-width: calc(1px * var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
border-color: var(--color-muted-1);
|
|
|
|
border-style: solid;
|
2023-05-05 14:14:42 +00:00
|
|
|
border-radius: calc(var(--radius-1) * var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
display: flex;
|
`ShapeUtil.getGeometry`, selection rewrite (#1751)
This PR is a significant rewrite of our selection / hit testing logic.
It
- replaces our current geometric helpers (`getBounds`, `getOutline`,
`hitTestPoint`, and `hitTestLineSegment`) with a new geometry API
- moves our hit testing entirely to JS using geometry
- improves selection logic, especially around editing shapes, groups and
frames
- fixes many minor selection bugs (e.g. shapes behind frames)
- removes hit-testing DOM elements from ShapeFill etc.
- adds many new tests around selection
- adds new tests around selection
- makes several superficial changes to surface editor APIs
This PR is hard to evaluate. The `selection-omnibus` test suite is
intended to describe all of the selection behavior, however all existing
tests are also either here preserved and passing or (in a few cases
around editing shapes) are modified to reflect the new behavior.
## Geometry
All `ShapeUtils` implement `getGeometry`, which returns a single
geometry primitive (`Geometry2d`). For example:
```ts
class BoxyShapeUtil {
getGeometry(shape: BoxyShape) {
return new Rectangle2d({
width: shape.props.width,
height: shape.props.height,
isFilled: true,
margin: shape.props.strokeWidth
})
}
}
```
This geometric primitive is used for all bounds calculation, hit
testing, intersection with arrows, etc.
There are several geometric primitives that extend `Geometry2d`:
- `Arc2d`
- `Circle2d`
- `CubicBezier2d`
- `CubicSpline2d`
- `Edge2d`
- `Ellipse2d`
- `Group2d`
- `Polygon2d`
- `Rectangle2d`
- `Stadium2d`
For shapes that have more complicated geometric representations, such as
an arrow with a label, the `Group2d` can accept other primitives as its
children.
## Hit testing
Previously, we did all hit testing via events set on shapes and other
elements. In this PR, I've replaced those hit tests with our own
calculation for hit tests in JavaScript. This removed the need for many
DOM elements, such as hit test area borders and fills which only existed
to trigger pointer events.
## Selection
We now support selecting "hollow" shapes by clicking inside of them.
This involves a lot of new logic but it should work intuitively. See
`Editor.getShapeAtPoint` for the (thoroughly commented) implementation.
![Kapture 2023-07-23 at 23 27
27](https://github.com/tldraw/tldraw/assets/23072548/a743275c-acdb-42d9-a3fe-b3e20dce86b6)
every sunset is actually the sun hiding in fear and respect of tldraw's
quality of interactions
This PR also fixes several bugs with scribble selection, in particular
around the shift key modifier.
![Kapture 2023-07-24 at 23 34
07](https://github.com/tldraw/tldraw/assets/23072548/871d67d0-8d06-42ae-a2b2-021effba37c5)
...as well as issues with labels and editing.
There are **over 100 new tests** for selection covering groups, frames,
brushing, scribbling, hovering, and editing. I'll add a few more before
I feel comfortable merging this PR.
## Arrow binding
Using the same "hollow shape" logic as selection, arrow binding is
significantly improved.
![Kapture 2023-07-22 at 07 46
25](https://github.com/tldraw/tldraw/assets/23072548/5aa724b3-b57d-4fb7-92d0-80e34246753c)
a thousand wise men could not improve on this
## Moving focus between editing shapes
Previously, this was handled in the `editing_shapes` state. This is
moved to `useEditableText`, and should generally be considered an
advanced implementation detail on a shape-by-shape basis. This addresses
a bug that I'd never noticed before, but which can be reproduced by
selecting an shape—but not focusing its input—while editing a different
shape. Previously, the new shape became the editing shape but its input
did not focus.
![Kapture 2023-07-23 at 23 19
09](https://github.com/tldraw/tldraw/assets/23072548/a5e157fb-24a8-42bd-a692-04ce769b1a9c)
In this PR, you can select a shape by clicking on its edge or body, or
select its input to transfer editing / focus.
![Kapture 2023-07-23 at 23 22
21](https://github.com/tldraw/tldraw/assets/23072548/7384e7ea-9777-4e1a-8f63-15de2166a53a)
tldraw, glorious tldraw
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Erase shapes
2. Select shapes
3. Calculate their bounding boxes
- [ ] Unit Tests // todo
- [ ] End to end tests // todo
### Release Notes
- [editor] Remove `ShapeUtil.getBounds`, `ShapeUtil.getOutline`,
`ShapeUtil.hitTestPoint`, `ShapeUtil.hitTestLineSegment`
- [editor] Add `ShapeUtil.getGeometry`
- [editor] Add `Editor.getShapeGeometry`
2023-07-25 16:10:15 +00:00
|
|
|
flex-direction: column;
|
2023-04-25 11:01:25 +00:00
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
`ShapeUtil.getGeometry`, selection rewrite (#1751)
This PR is a significant rewrite of our selection / hit testing logic.
It
- replaces our current geometric helpers (`getBounds`, `getOutline`,
`hitTestPoint`, and `hitTestLineSegment`) with a new geometry API
- moves our hit testing entirely to JS using geometry
- improves selection logic, especially around editing shapes, groups and
frames
- fixes many minor selection bugs (e.g. shapes behind frames)
- removes hit-testing DOM elements from ShapeFill etc.
- adds many new tests around selection
- adds new tests around selection
- makes several superficial changes to surface editor APIs
This PR is hard to evaluate. The `selection-omnibus` test suite is
intended to describe all of the selection behavior, however all existing
tests are also either here preserved and passing or (in a few cases
around editing shapes) are modified to reflect the new behavior.
## Geometry
All `ShapeUtils` implement `getGeometry`, which returns a single
geometry primitive (`Geometry2d`). For example:
```ts
class BoxyShapeUtil {
getGeometry(shape: BoxyShape) {
return new Rectangle2d({
width: shape.props.width,
height: shape.props.height,
isFilled: true,
margin: shape.props.strokeWidth
})
}
}
```
This geometric primitive is used for all bounds calculation, hit
testing, intersection with arrows, etc.
There are several geometric primitives that extend `Geometry2d`:
- `Arc2d`
- `Circle2d`
- `CubicBezier2d`
- `CubicSpline2d`
- `Edge2d`
- `Ellipse2d`
- `Group2d`
- `Polygon2d`
- `Rectangle2d`
- `Stadium2d`
For shapes that have more complicated geometric representations, such as
an arrow with a label, the `Group2d` can accept other primitives as its
children.
## Hit testing
Previously, we did all hit testing via events set on shapes and other
elements. In this PR, I've replaced those hit tests with our own
calculation for hit tests in JavaScript. This removed the need for many
DOM elements, such as hit test area borders and fills which only existed
to trigger pointer events.
## Selection
We now support selecting "hollow" shapes by clicking inside of them.
This involves a lot of new logic but it should work intuitively. See
`Editor.getShapeAtPoint` for the (thoroughly commented) implementation.
![Kapture 2023-07-23 at 23 27
27](https://github.com/tldraw/tldraw/assets/23072548/a743275c-acdb-42d9-a3fe-b3e20dce86b6)
every sunset is actually the sun hiding in fear and respect of tldraw's
quality of interactions
This PR also fixes several bugs with scribble selection, in particular
around the shift key modifier.
![Kapture 2023-07-24 at 23 34
07](https://github.com/tldraw/tldraw/assets/23072548/871d67d0-8d06-42ae-a2b2-021effba37c5)
...as well as issues with labels and editing.
There are **over 100 new tests** for selection covering groups, frames,
brushing, scribbling, hovering, and editing. I'll add a few more before
I feel comfortable merging this PR.
## Arrow binding
Using the same "hollow shape" logic as selection, arrow binding is
significantly improved.
![Kapture 2023-07-22 at 07 46
25](https://github.com/tldraw/tldraw/assets/23072548/5aa724b3-b57d-4fb7-92d0-80e34246753c)
a thousand wise men could not improve on this
## Moving focus between editing shapes
Previously, this was handled in the `editing_shapes` state. This is
moved to `useEditableText`, and should generally be considered an
advanced implementation detail on a shape-by-shape basis. This addresses
a bug that I'd never noticed before, but which can be reproduced by
selecting an shape—but not focusing its input—while editing a different
shape. Previously, the new shape became the editing shape but its input
did not focus.
![Kapture 2023-07-23 at 23 19
09](https://github.com/tldraw/tldraw/assets/23072548/a5e157fb-24a8-42bd-a692-04ce769b1a9c)
In this PR, you can select a shape by clicking on its edge or body, or
select its input to transfer editing / focus.
![Kapture 2023-07-23 at 23 22
21](https://github.com/tldraw/tldraw/assets/23072548/7384e7ea-9777-4e1a-8f63-15de2166a53a)
tldraw, glorious tldraw
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Erase shapes
2. Select shapes
3. Calculate their bounding boxes
- [ ] Unit Tests // todo
- [ ] End to end tests // todo
### Release Notes
- [editor] Remove `ShapeUtil.getBounds`, `ShapeUtil.getOutline`,
`ShapeUtil.hitTestPoint`, `ShapeUtil.hitTestLineSegment`
- [editor] Add `ShapeUtil.getGeometry`
- [editor] Add `Editor.getShapeGeometry`
2023-07-25 16:10:15 +00:00
|
|
|
text-align: left;
|
2023-04-25 11:01:25 +00:00
|
|
|
position: relative;
|
|
|
|
pointer-events: all;
|
|
|
|
overflow: hidden;
|
|
|
|
padding: var(--space-2);
|
|
|
|
}
|
|
|
|
|
`ShapeUtil.getGeometry`, selection rewrite (#1751)
This PR is a significant rewrite of our selection / hit testing logic.
It
- replaces our current geometric helpers (`getBounds`, `getOutline`,
`hitTestPoint`, and `hitTestLineSegment`) with a new geometry API
- moves our hit testing entirely to JS using geometry
- improves selection logic, especially around editing shapes, groups and
frames
- fixes many minor selection bugs (e.g. shapes behind frames)
- removes hit-testing DOM elements from ShapeFill etc.
- adds many new tests around selection
- adds new tests around selection
- makes several superficial changes to surface editor APIs
This PR is hard to evaluate. The `selection-omnibus` test suite is
intended to describe all of the selection behavior, however all existing
tests are also either here preserved and passing or (in a few cases
around editing shapes) are modified to reflect the new behavior.
## Geometry
All `ShapeUtils` implement `getGeometry`, which returns a single
geometry primitive (`Geometry2d`). For example:
```ts
class BoxyShapeUtil {
getGeometry(shape: BoxyShape) {
return new Rectangle2d({
width: shape.props.width,
height: shape.props.height,
isFilled: true,
margin: shape.props.strokeWidth
})
}
}
```
This geometric primitive is used for all bounds calculation, hit
testing, intersection with arrows, etc.
There are several geometric primitives that extend `Geometry2d`:
- `Arc2d`
- `Circle2d`
- `CubicBezier2d`
- `CubicSpline2d`
- `Edge2d`
- `Ellipse2d`
- `Group2d`
- `Polygon2d`
- `Rectangle2d`
- `Stadium2d`
For shapes that have more complicated geometric representations, such as
an arrow with a label, the `Group2d` can accept other primitives as its
children.
## Hit testing
Previously, we did all hit testing via events set on shapes and other
elements. In this PR, I've replaced those hit tests with our own
calculation for hit tests in JavaScript. This removed the need for many
DOM elements, such as hit test area borders and fills which only existed
to trigger pointer events.
## Selection
We now support selecting "hollow" shapes by clicking inside of them.
This involves a lot of new logic but it should work intuitively. See
`Editor.getShapeAtPoint` for the (thoroughly commented) implementation.
![Kapture 2023-07-23 at 23 27
27](https://github.com/tldraw/tldraw/assets/23072548/a743275c-acdb-42d9-a3fe-b3e20dce86b6)
every sunset is actually the sun hiding in fear and respect of tldraw's
quality of interactions
This PR also fixes several bugs with scribble selection, in particular
around the shift key modifier.
![Kapture 2023-07-24 at 23 34
07](https://github.com/tldraw/tldraw/assets/23072548/871d67d0-8d06-42ae-a2b2-021effba37c5)
...as well as issues with labels and editing.
There are **over 100 new tests** for selection covering groups, frames,
brushing, scribbling, hovering, and editing. I'll add a few more before
I feel comfortable merging this PR.
## Arrow binding
Using the same "hollow shape" logic as selection, arrow binding is
significantly improved.
![Kapture 2023-07-22 at 07 46
25](https://github.com/tldraw/tldraw/assets/23072548/5aa724b3-b57d-4fb7-92d0-80e34246753c)
a thousand wise men could not improve on this
## Moving focus between editing shapes
Previously, this was handled in the `editing_shapes` state. This is
moved to `useEditableText`, and should generally be considered an
advanced implementation detail on a shape-by-shape basis. This addresses
a bug that I'd never noticed before, but which can be reproduced by
selecting an shape—but not focusing its input—while editing a different
shape. Previously, the new shape became the editing shape but its input
did not focus.
![Kapture 2023-07-23 at 23 19
09](https://github.com/tldraw/tldraw/assets/23072548/a5e157fb-24a8-42bd-a692-04ce769b1a9c)
In this PR, you can select a shape by clicking on its edge or body, or
select its input to transfer editing / focus.
![Kapture 2023-07-23 at 23 22
21](https://github.com/tldraw/tldraw/assets/23072548/7384e7ea-9777-4e1a-8f63-15de2166a53a)
tldraw, glorious tldraw
### Change Type
- [x] `major` — Breaking change
### Test Plan
1. Erase shapes
2. Select shapes
3. Calculate their bounding boxes
- [ ] Unit Tests // todo
- [ ] End to end tests // todo
### Release Notes
- [editor] Remove `ShapeUtil.getBounds`, `ShapeUtil.getOutline`,
`ShapeUtil.hitTestPoint`, `ShapeUtil.hitTestLineSegment`
- [editor] Add `ShapeUtil.getGeometry`
- [editor] Add `Editor.getShapeGeometry`
2023-07-25 16:10:15 +00:00
|
|
|
.tl-shape-error-boundary::before {
|
2023-05-05 14:14:42 +00:00
|
|
|
transform: scale(var(--tl-scale));
|
2023-04-25 11:01:25 +00:00
|
|
|
content: 'Error';
|
|
|
|
font-size: 12px;
|
|
|
|
font-family: inherit;
|
|
|
|
color: var(--color-text-0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------- Error Boundary ----------------- */
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary {
|
2023-04-25 11:01:25 +00:00
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
padding: var(--space-4);
|
|
|
|
background-color: var(--color-background);
|
|
|
|
color: var(--color-text-1);
|
|
|
|
position: absolute;
|
2023-05-05 14:14:42 +00:00
|
|
|
z-index: 600;
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__overlay {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
inset: 0px;
|
2023-05-11 22:14:58 +00:00
|
|
|
z-index: 500;
|
2023-04-25 11:01:25 +00:00
|
|
|
background-color: var(--color-overlay);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content * {
|
2023-04-25 11:01:25 +00:00
|
|
|
user-select: all;
|
|
|
|
-webkit-user-select: text;
|
|
|
|
pointer-events: all;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__canvas {
|
2023-04-25 11:01:25 +00:00
|
|
|
pointer-events: none;
|
|
|
|
position: absolute;
|
2023-05-16 08:37:37 +00:00
|
|
|
inset: 0px;
|
2023-04-25 11:01:25 +00:00
|
|
|
z-index: -1;
|
|
|
|
}
|
|
|
|
/* some browsers seem to have some weird interactions between stacking contexts
|
|
|
|
and pointer-events. this ::after pseudo element covers the canvas and prevents
|
|
|
|
it from receiving any pointer events or affecting the cursor. */
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__canvas::after {
|
2023-04-25 11:01:25 +00:00
|
|
|
content: ' ';
|
|
|
|
display: block;
|
|
|
|
position: absolute;
|
2023-05-16 08:37:37 +00:00
|
|
|
inset: 0px;
|
2023-05-05 14:14:42 +00:00
|
|
|
z-index: 600;
|
2023-08-25 16:22:52 +00:00
|
|
|
pointer-events: all;
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content {
|
2023-04-25 11:01:25 +00:00
|
|
|
width: fit-content;
|
|
|
|
height: fit-content;
|
|
|
|
max-width: 100%;
|
|
|
|
width: 400px;
|
|
|
|
max-height: 100%;
|
|
|
|
background-color: var(--color-panel);
|
|
|
|
padding: var(--space-6);
|
|
|
|
border-radius: var(--radius-4);
|
|
|
|
box-shadow: var(--shadow-2);
|
|
|
|
font-size: 14px;
|
|
|
|
font-weight: 400;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
gap: var(--space-5);
|
|
|
|
overflow: auto;
|
2023-05-11 22:14:58 +00:00
|
|
|
z-index: 600;
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content__expanded {
|
2023-04-25 11:01:25 +00:00
|
|
|
width: 600px;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content h2 {
|
2023-04-25 11:01:25 +00:00
|
|
|
font-size: 16px;
|
|
|
|
margin: 0px;
|
|
|
|
font-weight: 500;
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content p {
|
2023-04-25 11:01:25 +00:00
|
|
|
line-height: 1.5;
|
2023-05-16 08:37:37 +00:00
|
|
|
margin: 0px;
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content pre {
|
2023-04-25 11:01:25 +00:00
|
|
|
background-color: var(--color-muted-2);
|
|
|
|
padding: var(--space-5);
|
|
|
|
border-radius: var(--radius-2);
|
|
|
|
overflow: auto;
|
|
|
|
font-size: 12px;
|
|
|
|
max-height: 320px;
|
2023-05-16 08:37:37 +00:00
|
|
|
margin: 0px;
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content button {
|
2023-04-25 11:01:25 +00:00
|
|
|
background: none;
|
|
|
|
border: none;
|
|
|
|
font-family: inherit;
|
|
|
|
font-size: 14px;
|
|
|
|
font-weight: 500;
|
|
|
|
padding: var(--space-4);
|
|
|
|
border-radius: var(--radius-3);
|
2023-05-23 14:12:11 +00:00
|
|
|
cursor: var(--tl-cursor-pointer);
|
2023-04-25 11:01:25 +00:00
|
|
|
color: inherit;
|
|
|
|
background-color: transparent;
|
|
|
|
}
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content button:hover {
|
2023-04-25 11:01:25 +00:00
|
|
|
background-color: var(--color-low);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content a {
|
2023-04-25 11:01:25 +00:00
|
|
|
color: var(--color-text-1);
|
|
|
|
font-weight: 500;
|
|
|
|
text-decoration: none;
|
|
|
|
}
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content a:hover {
|
2023-04-25 11:01:25 +00:00
|
|
|
color: var(--color-text-2);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content__error {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: relative;
|
|
|
|
}
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content__error button {
|
2023-04-25 11:01:25 +00:00
|
|
|
position: absolute;
|
|
|
|
top: var(--space-2);
|
|
|
|
right: var(--space-2);
|
|
|
|
font-size: 12px;
|
|
|
|
padding: var(--space-2) var(--space-3);
|
|
|
|
background-color: var(--color-panel);
|
|
|
|
border-radius: var(--radius-1);
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content__actions {
|
2023-04-25 11:01:25 +00:00
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
gap: var(--space-4);
|
|
|
|
margin: calc(var(--space-4) * -1);
|
2023-05-16 08:37:37 +00:00
|
|
|
margin-top: 0px;
|
2023-04-25 11:01:25 +00:00
|
|
|
}
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content__actions__group {
|
2023-04-25 11:01:25 +00:00
|
|
|
display: flex;
|
|
|
|
gap: var(--space-4);
|
|
|
|
}
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content .tl-error-boundary__reset {
|
2023-04-25 11:01:25 +00:00
|
|
|
color: var(--color-warn);
|
|
|
|
}
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content .tl-error-boundary__refresh {
|
2023-04-25 11:01:25 +00:00
|
|
|
background-color: var(--color-primary);
|
|
|
|
color: var(--color-selected-contrast);
|
|
|
|
}
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-error-boundary__content .tl-error-boundary__refresh:hover {
|
2023-04-25 11:01:25 +00:00
|
|
|
background-color: var(--color-primary);
|
|
|
|
opacity: 0.9;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --------------------- Coarse --------------------- */
|
|
|
|
|
|
|
|
@media screen and (pointer: coarse) {
|
|
|
|
/* If mobile always show handle-hint as there is no hover state */
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-canvas__mobile .tl-handle__hint {
|
2023-04-25 11:01:25 +00:00
|
|
|
opacity: 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:14:42 +00:00
|
|
|
.tl-hidden {
|
2023-04-25 11:01:25 +00:00
|
|
|
opacity: 0;
|
|
|
|
pointer-events: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
.debug__ui-logger {
|
|
|
|
position: absolute;
|
|
|
|
top: 62px;
|
|
|
|
left: 16px;
|
|
|
|
color: #555;
|
|
|
|
font-size: 12px;
|
|
|
|
font-family: monospace;
|
|
|
|
}
|