Browse Source

fixes zstacking with text

main
Steve Ruiz 4 years ago
parent
commit
d0d24e9c71

+ 3
- 1
components/canvas/bounds/bounding-box.tsx View File

@@ -10,7 +10,6 @@ import {
10 10
   getSelectedShapes,
11 11
   isMobile,
12 12
 } from 'utils/utils'
13
-
14 13
 import CenterHandle from './center-handle'
15 14
 import CornerHandle from './corner-handle'
16 15
 import EdgeHandle from './edge-handle'
@@ -18,8 +17,11 @@ import RotateHandle from './rotate-handle'
18 17
 
19 18
 export default function Bounds() {
20 19
   const isBrushing = useSelector((s) => s.isIn('brushSelecting'))
20
+
21 21
   const isSelecting = useSelector((s) => s.isIn('selecting'))
22
+
22 23
   const zoom = useSelector((s) => getCurrentCamera(s.data).zoom)
24
+
23 25
   const bounds = useSelector((s) => s.values.selectedBounds)
24 26
 
25 27
   const selectedIds = useSelector(

+ 1
- 1
components/canvas/canvas.tsx View File

@@ -33,7 +33,7 @@ export default function Canvas() {
33 33
           <g ref={rGroup}>
34 34
             <BoundsBg />
35 35
             <Page />
36
-            <Selected />
36
+            {/* <Selected /> */}
37 37
             <Bounds />
38 38
             <Handles />
39 39
             <Brush />

+ 3
- 3
components/canvas/selected.tsx View File

@@ -46,8 +46,8 @@ export const ShapeOutline = memo(function ShapeOutline({ id }: { id: string }) {
46 46
   const center = getShapeUtils(shape).getCenter(shape)
47 47
 
48 48
   const transform = `
49
-  rotate(${shape.rotation * (180 / Math.PI)}, ${center})
50
-  translate(${shape.point})
49
+    rotate(${shape.rotation * (180 / Math.PI)}, ${center})
50
+    translate(${shape.point})
51 51
   `
52 52
 
53 53
   return (
@@ -68,7 +68,7 @@ const SelectIndicator = styled('path', {
68 68
   strokeLinejoin: 'round',
69 69
   stroke: '$selected',
70 70
   pointerEvents: 'none',
71
-  fill: 'none',
71
+  fill: 'transparent',
72 72
 
73 73
   variants: {
74 74
     isLocked: {

+ 3
- 35
components/tools-panel/tools-panel.tsx View File

@@ -50,7 +50,7 @@ export default function ToolsPanel() {
50 50
             <IconButton
51 51
               name="select"
52 52
               bp={{ '@initial': 'mobile', '@sm': 'small' }}
53
-              size={{ '@initial': 'small', '@sm': 'small', '@md': 'large' }}
53
+              size={{ '@initial': 'small', '@sm': 'small', '@md': 'medium' }}
54 54
               onClick={selectSelectTool}
55 55
               isActive={activeTool === 'select'}
56 56
             >
@@ -114,44 +114,12 @@ export default function ToolsPanel() {
114 114
               <TextIcon />
115 115
             </IconButton>
116 116
           </Tooltip>
117
-          {/* <IconButton
118
-            name={ShapeType.Circle}
119
-            size={{ '@initial': 'medium', '@sm': 'small', '@md': 'large' }}
120
-            onClick={selectCircleTool}
121
-            isActive={activeTool === ShapeType.Circle}
122
-          >
123
-            <CircleIcon />
124
-          </IconButton> */}
125
-          {/* <IconButton
126
-            name={ShapeType.Line}
127
-            size={{ '@initial': 'medium', '@sm': 'small', '@md': 'large' }}
128
-            onClick={selectLineTool}
129
-            isActive={activeTool === ShapeType.Line}
130
-          >
131
-            <DividerHorizontalIcon transform="rotate(-45)" />
132
-          </IconButton>
133
-          <IconButton
134
-            name={ShapeType.Ray}
135
-            size={{ '@initial': 'medium', '@sm': 'small', '@md': 'large' }}
136
-            onClick={selectRayTool}
137
-            isActive={activeTool === ShapeType.Ray}
138
-          >
139
-            <SewingPinIcon transform="rotate(-135)" />
140
-          </IconButton>
141
-          <IconButton
142
-            name={ShapeType.Dot}
143
-            size={{ '@initial': 'medium', '@sm': 'small', '@md': 'large' }}
144
-            onClick={selectDotTool}
145
-            isActive={activeTool === ShapeType.Dot}
146
-          >
147
-            <DotIcon />
148
-          </IconButton> */}
149 117
         </Container>
150 118
         <Container>
151 119
           <Tooltip label="Lock Tool">
152 120
             <IconButton
153 121
               bp={{ '@initial': 'mobile', '@sm': 'small' }}
154
-              size={{ '@initial': 'small', '@sm': 'small', '@md': 'large' }}
122
+              size={{ '@initial': 'small', '@sm': 'small', '@md': 'medium' }}
155 123
               onClick={selectToolLock}
156 124
             >
157 125
               {isToolLocked ? <LockClosedIcon /> : <LockOpen1Icon />}
@@ -161,7 +129,7 @@ export default function ToolsPanel() {
161 129
             <Tooltip label="Unlock Pen">
162 130
               <IconButton
163 131
                 bp={{ '@initial': 'mobile', '@sm': 'small' }}
164
-                size={{ '@initial': 'small', '@sm': 'small', '@md': 'large' }}
132
+                size={{ '@initial': 'small', '@sm': 'small', '@md': 'medium' }}
165 133
                 onClick={selectToolLock}
166 134
               >
167 135
                 <Pencil2Icon />

+ 1
- 1
lib/shape-styles.ts View File

@@ -70,7 +70,7 @@ export function getFontStyle(
70 70
 ) {
71 71
   const fontSize = getFontSize(size)
72 72
 
73
-  return `${fontSize * scale}px Verveine Regular`
73
+  return `${fontSize * scale}px/1.4 Verveine Regular`
74 74
 }
75 75
 
76 76
 export function getShapeStyle(

+ 75
- 23
lib/shape-utils/text.tsx View File

@@ -1,34 +1,41 @@
1 1
 import { uniqueId } from 'utils/utils'
2
-import vec from 'utils/vec'
3 2
 import { TextShape, ShapeType, FontSize } from 'types'
4 3
 import { registerShapeUtils } from './index'
5
-import { defaultStyle, getFontStyle, getShapeStyle } from 'lib/shape-styles'
4
+import {
5
+  defaultStyle,
6
+  getFontSize,
7
+  getFontStyle,
8
+  getShapeStyle,
9
+} from 'lib/shape-styles'
6 10
 import styled from 'styles'
7 11
 import state from 'state'
8
-import { useEffect, useRef } from 'react'
9
-
10
-// A div used for measurement
12
+import React from 'react'
11 13
 
12 14
 if (document.getElementById('__textMeasure')) {
13 15
   document.getElementById('__textMeasure').remove()
14 16
 }
15 17
 
18
+// A div used for measurement
16 19
 const mdiv = document.createElement('pre')
17 20
 mdiv.id = '__textMeasure'
18 21
 mdiv.style.whiteSpace = 'pre'
19 22
 mdiv.style.width = 'auto'
20 23
 mdiv.style.border = '1px solid red'
21 24
 mdiv.style.padding = '4px'
22
-mdiv.style.lineHeight = '1'
23 25
 mdiv.style.margin = '0px'
24 26
 mdiv.style.opacity = '0'
25 27
 mdiv.style.position = 'absolute'
26 28
 mdiv.style.top = '-500px'
27 29
 mdiv.style.left = '0px'
28 30
 mdiv.style.zIndex = '9999'
31
+mdiv.tabIndex = -1
29 32
 mdiv.setAttribute('readonly', 'true')
30 33
 document.body.appendChild(mdiv)
31 34
 
35
+function normalizeText(text: string) {
36
+  return text.replace(/\t/g, '        ').replace(/\r?\n|\r/g, '\n')
37
+}
38
+
32 39
 const text = registerShapeUtils<TextShape>({
33 40
   isForeignObject: true,
34 41
   canChangeAspectRatio: false,
@@ -66,6 +73,51 @@ const text = registerShapeUtils<TextShape>({
66 73
 
67 74
     const bounds = this.getBounds(shape)
68 75
 
76
+    function handleChange(e: React.ChangeEvent<HTMLTextAreaElement>) {
77
+      state.send('EDITED_SHAPE', {
78
+        change: { text: normalizeText(e.currentTarget.value) },
79
+      })
80
+    }
81
+
82
+    function handleKeyDown(e: React.KeyboardEvent) {
83
+      e.stopPropagation()
84
+      if (e.key === 'Tab') {
85
+        e.preventDefault()
86
+      }
87
+    }
88
+
89
+    function handleBlur() {
90
+      state.send('BLURRED_EDITING_SHAPE')
91
+    }
92
+
93
+    function handleFocus(e: React.FocusEvent<HTMLTextAreaElement>) {
94
+      e.currentTarget.select()
95
+      state.send('FOCUSED_EDITING_SHAPE')
96
+    }
97
+
98
+    const lineHeight = getFontSize(shape.fontSize) * shape.scale
99
+    const gap = lineHeight * 0.4
100
+
101
+    if (!isEditing) {
102
+      return (
103
+        <g id={id} pointerEvents="none">
104
+          {text.split('\n').map((str, i) => (
105
+            <text
106
+              key={i}
107
+              x={4}
108
+              y={4 + gap / 2 + i * (lineHeight + gap)}
109
+              style={{ font }}
110
+              width={bounds.width}
111
+              height={bounds.height}
112
+              dominant-baseline="hanging"
113
+            >
114
+              {str}
115
+            </text>
116
+          ))}
117
+        </g>
118
+      )
119
+    }
120
+
69 121
     return (
70 122
       <foreignObject
71 123
         id={id}
@@ -82,22 +134,17 @@ const text = registerShapeUtils<TextShape>({
82 134
               font,
83 135
               color: styles.stroke,
84 136
             }}
137
+            tabIndex={0}
85 138
             value={text}
86 139
             autoComplete="false"
87 140
             autoCapitalize="false"
88 141
             autoCorrect="false"
89
-            onFocus={(e) => {
90
-              e.currentTarget.select()
91
-              state.send('FOCUSED_EDITING_SHAPE')
92
-            }}
93
-            onBlur={() => {
94
-              state.send('BLURRED_EDITING_SHAPE')
95
-            }}
96
-            onChange={(e) => {
97
-              state.send('EDITED_SHAPE', {
98
-                change: { text: e.currentTarget.value },
99
-              })
100
-            }}
142
+            spellCheck="false"
143
+            onFocus={handleFocus}
144
+            onBlur={handleBlur}
145
+            onKeyDown={handleKeyDown}
146
+            onChange={handleChange}
147
+            dir="auto"
101 148
           />
102 149
         ) : (
103 150
           <StyledText
@@ -115,7 +162,7 @@ const text = registerShapeUtils<TextShape>({
115 162
 
116 163
   getBounds(shape) {
117 164
     if (!this.boundsCache.has(shape)) {
118
-      mdiv.innerHTML = shape.text || ' ' // + '&nbsp;'
165
+      mdiv.innerHTML = shape.text + '&zwj;'
119 166
       mdiv.style.font = getFontStyle(shape.fontSize, shape.scale, shape.style)
120 167
 
121 168
       const [minX, minY] = shape.point
@@ -180,14 +227,17 @@ const StyledText = styled('div', {
180 227
   border: 'none',
181 228
   padding: '4px',
182 229
   whiteSpace: 'pre',
183
-  resize: 'none',
184 230
   minHeight: 1,
185 231
   minWidth: 1,
186
-  outline: 'none',
232
+  outline: 0,
187 233
   backgroundColor: 'transparent',
188 234
   overflow: 'hidden',
189 235
   pointerEvents: 'none',
190 236
   userSelect: 'none',
237
+  backfaceVisibility: 'hidden',
238
+  display: 'inline-block',
239
+  position: 'relative',
240
+  zIndex: 0,
191 241
 })
192 242
 
193 243
 const StyledTextArea = styled('textarea', {
@@ -199,8 +249,10 @@ const StyledTextArea = styled('textarea', {
199 249
   resize: 'none',
200 250
   minHeight: 1,
201 251
   minWidth: 1,
202
-  outline: 'none',
203
-  overflow: 'hidden',
252
+  outline: 0,
204 253
   backgroundColor: '$boundsBg',
254
+  overflow: 'hidden',
205 255
   pointerEvents: 'all',
256
+  backfaceVisibility: 'hidden',
257
+  display: 'inline-block',
206 258
 })

+ 12
- 14
state/state.ts View File

@@ -155,9 +155,18 @@ const state = createState({
155 155
         CREATED_PAGE: ['clearSelectedIds', 'createPage'],
156 156
         DELETED_PAGE: { unless: 'hasOnlyOnePage', do: 'deletePage' },
157 157
         LOADED_FROM_FILE: ['loadDocumentFromJson', 'resetHistory'],
158
-        PANNED_CAMERA: {
159
-          do: 'panCamera',
160
-        },
158
+        PANNED_CAMERA: 'panCamera',
159
+        SELECTED_SELECT_TOOL: { to: 'selecting' },
160
+        SELECTED_DRAW_TOOL: { unless: 'isReadOnly', to: 'draw' },
161
+        SELECTED_ARROW_TOOL: { unless: 'isReadOnly', to: 'arrow' },
162
+        SELECTED_DOT_TOOL: { unless: 'isReadOnly', to: 'dot' },
163
+        SELECTED_CIRCLE_TOOL: { unless: 'isReadOnly', to: 'circle' },
164
+        SELECTED_ELLIPSE_TOOL: { unless: 'isReadOnly', to: 'ellipse' },
165
+        SELECTED_RAY_TOOL: { unless: 'isReadOnly', to: 'ray' },
166
+        SELECTED_LINE_TOOL: { unless: 'isReadOnly', to: 'line' },
167
+        SELECTED_POLYLINE_TOOL: { unless: 'isReadOnly', to: 'polyline' },
168
+        SELECTED_RECTANGLE_TOOL: { unless: 'isReadOnly', to: 'rectangle' },
169
+        SELECTED_TEXT_TOOL: { unless: 'isReadOnly', to: 'text' },
161 170
       },
162 171
       initial: 'selecting',
163 172
       states: {
@@ -197,17 +206,6 @@ const state = createState({
197 206
             },
198 207
             SELECTED_ALL: { to: 'selecting', do: 'selectAll' },
199 208
             NUDGED: { do: 'nudgeSelection' },
200
-            SELECTED_SELECT_TOOL: { to: 'selecting' },
201
-            SELECTED_DRAW_TOOL: { unless: 'isReadOnly', to: 'draw' },
202
-            SELECTED_ARROW_TOOL: { unless: 'isReadOnly', to: 'arrow' },
203
-            SELECTED_DOT_TOOL: { unless: 'isReadOnly', to: 'dot' },
204
-            SELECTED_CIRCLE_TOOL: { unless: 'isReadOnly', to: 'circle' },
205
-            SELECTED_ELLIPSE_TOOL: { unless: 'isReadOnly', to: 'ellipse' },
206
-            SELECTED_RAY_TOOL: { unless: 'isReadOnly', to: 'ray' },
207
-            SELECTED_LINE_TOOL: { unless: 'isReadOnly', to: 'line' },
208
-            SELECTED_POLYLINE_TOOL: { unless: 'isReadOnly', to: 'polyline' },
209
-            SELECTED_RECTANGLE_TOOL: { unless: 'isReadOnly', to: 'rectangle' },
210
-            SELECTED_TEXT_TOOL: { unless: 'isReadOnly', to: 'text' },
211 209
             ZOOMED_CAMERA: {
212 210
               do: 'zoomCamera',
213 211
             },

Loading…
Cancel
Save