Przeglądaj źródła

[Bug] Added visibility of clone tool (#381)

* [bug] Added display of clone buttons

* Reverted changes from sticky notes

* feat show those style options when the text tool is selected

* Add clone handles to sticky

Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
main
Proful Sadangi 3 lat temu
rodzic
commit
7563575ef5
No account linked to committer's email address

+ 7
- 8
packages/tldraw/src/components/TopPanel/StyleMenu/StyleMenu.tsx Wyświetl plik

@@ -2,12 +2,7 @@ import * as React from 'react'
2 2
 import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
3 3
 import { strokes, fills, defaultTextStyle } from '~state/shapes/shared/shape-styles'
4 4
 import { useTldrawApp } from '~hooks'
5
-import {
6
-  DMCheckboxItem,
7
-  DMContent,
8
-  DMRadioItem,
9
-  DMTriggerIcon,
10
-} from '~components/Primitives/DropdownMenu'
5
+import { DMCheckboxItem, DMContent, DMRadioItem } from '~components/Primitives/DropdownMenu'
11 6
 import {
12 7
   CircleIcon,
13 8
   DashDashedIcon,
@@ -69,9 +64,13 @@ const ALIGN_ICONS = {
69 64
 const themeSelector = (s: TDSnapshot) => (s.settings.isDarkMode ? 'dark' : 'light')
70 65
 
71 66
 const showTextStylesSelector = (s: TDSnapshot) => {
72
-  const pageId = s.appState.currentPageId
67
+  const { activeTool, currentPageId: pageId } = s.appState
73 68
   const page = s.document.pages[pageId]
74
-  return s.document.pageStates[pageId].selectedIds.some((id) => 'text' in page.shapes[id])
69
+
70
+  return (
71
+    activeTool === 'text' ||
72
+    s.document.pageStates[pageId].selectedIds.some((id) => 'text' in page.shapes[id])
73
+  )
75 74
 }
76 75
 
77 76
 export const StyleMenu = React.memo(function ColorMenu(): JSX.Element {

+ 5
- 1
packages/tldraw/src/state/TldrawApp.ts Wyświetl plik

@@ -2917,7 +2917,11 @@ export class TldrawApp extends StateManager<TDSnapshot> {
2917 2917
     this.currentTool.onShapeBlur?.()
2918 2918
   }
2919 2919
 
2920
-  onShapeClone: TLShapeCloneHandler = (info, e) => this.currentTool.onShapeClone?.(info, e)
2920
+  onShapeClone: TLShapeCloneHandler = (info, e) => {
2921
+    this.originPoint = this.getPagePoint(info.point)
2922
+    this.updateInputs(info, e)
2923
+    this.currentTool.onShapeClone?.(info, e)
2924
+  }
2921 2925
 
2922 2926
   onRenderCountChange = (ids: string[]) => {
2923 2927
     const appState = this.getAppState()

+ 2
- 0
packages/tldraw/src/state/shapes/DrawUtil/DrawUtil.tsx Wyświetl plik

@@ -31,6 +31,8 @@ export class DrawUtil extends TDShapeUtil<T, E> {
31 31
 
32 32
   pointCache: Record<string, number[]> = {}
33 33
 
34
+  canClone = true
35
+
34 36
   getShape = (props: Partial<T>): T => {
35 37
     return Utils.deepMerge<T>(
36 38
       {

+ 2
- 0
packages/tldraw/src/state/shapes/RectangleUtil/RectangleUtil.tsx Wyświetl plik

@@ -21,6 +21,8 @@ export class RectangleUtil extends TDShapeUtil<T, E> {
21 21
 
22 22
   canBind = true
23 23
 
24
+  canClone = true
25
+
24 26
   getShape = (props: Partial<T>): T => {
25 27
     return Utils.deepMerge<T>(
26 28
       {

+ 4
- 0
packages/tldraw/src/state/shapes/StickyUtil/StickyUtil.tsx Wyświetl plik

@@ -22,8 +22,12 @@ export class StickyUtil extends TDShapeUtil<T, E> {
22 22
 
23 23
   canEdit = true
24 24
 
25
+  canClone = true
26
+
25 27
   hideResizeHandles = true
26 28
 
29
+  showCloneHandles = true
30
+
27 31
   getShape = (props: Partial<T>): T => {
28 32
     return Utils.deepMerge<T>(
29 33
       {

+ 2
- 0
packages/tldraw/src/state/shapes/TextUtil/TextUtil.tsx Wyświetl plik

@@ -24,6 +24,8 @@ export class TextUtil extends TDShapeUtil<T, E> {
24 24
 
25 25
   canBind = true
26 26
 
27
+  canClone = true
28
+
27 29
   getShape = (props: Partial<T>): T => {
28 30
     return Utils.deepMerge<T>(
29 31
       {

+ 4
- 4
packages/tldraw/src/state/tools/SelectTool/SelectTool.ts Wyświetl plik

@@ -191,15 +191,15 @@ export class SelectTool extends BaseTool<Status> {
191 191
     if (key === 'Tab') {
192 192
       if (this.status === Status.Idle && this.app.selectedIds.length === 1) {
193 193
         const [selectedId] = this.app.selectedIds
194
-
195 194
         const clonedShape = this.getShapeClone(selectedId, 'right')
196 195
 
197 196
         if (clonedShape) {
198 197
           this.app.createShapes(clonedShape)
199
-
200 198
           this.setStatus(Status.Idle)
201
-          this.app.setEditingId(clonedShape.id)
202
-          this.app.select(clonedShape.id)
199
+          if (clonedShape.type === TDShapeType.Sticky) {
200
+            this.app.select(clonedShape.id)
201
+            this.app.setEditingId(clonedShape.id)
202
+          }
203 203
         }
204 204
       }
205 205
 

+ 0
- 338
replace-paths.ts Wyświetl plik

@@ -1,338 +0,0 @@
1
-#! /usr/bin/env node
2
-
3
-import * as program from 'commander'
4
-import { existsSync, readFileSync, writeFileSync } from 'fs'
5
-import { sync } from 'globby'
6
-import { dirname, relative, resolve } from 'path'
7
-import path from 'path'
8
-import fs from 'fs'
9
-import JSON5 from 'json5'
10
-
11
-/*
12
-"baseUrl": ".",
13
-"outDir": "lib",
14
-"paths": {
15
-  "src/*": ["src/*"]
16
-},
17
-*/
18
-
19
-export interface IRawTSConfig {
20
-  extends?: string
21
-  compilerOptions?: {
22
-    baseUrl?: string
23
-    outDir?: string
24
-    rootDir?: string
25
-    paths?: { [key: string]: string[] }
26
-  }
27
-}
28
-
29
-export interface ITSConfig {
30
-  baseUrl?: string
31
-  outDir?: string
32
-  rootDir?: string
33
-  compilerOptions?: Record<string, unknown>
34
-  paths?: { [key: string]: string[] }
35
-}
36
-
37
-export const mapPaths = (
38
-  paths: { [key: string]: string[] },
39
-  mapper: (x: string) => string
40
-): { [key: string]: string[] } => {
41
-  const dest = {} as { [key: string]: string[] }
42
-  Object.keys(paths).forEach((key) => {
43
-    dest[key] = paths[key].map(mapper)
44
-  })
45
-  return dest
46
-}
47
-
48
-export const loadConfig = (file: string): ITSConfig => {
49
-  const fileToParse = fs.readFileSync(file)
50
-
51
-  const parsedJsonFile = JSON5.parse(fileToParse as unknown as string)
52
-
53
-  const {
54
-    extends: extendsPath,
55
-    compilerOptions: { baseUrl, outDir, rootDir, paths } = {
56
-      baseUrl: undefined,
57
-      outDir: undefined,
58
-      rootDir: undefined,
59
-      paths: undefined,
60
-    },
61
-  } = parsedJsonFile as IRawTSConfig
62
-
63
-  const config: ITSConfig = {}
64
-  if (baseUrl) {
65
-    config.baseUrl = baseUrl
66
-  }
67
-  if (outDir) {
68
-    config.outDir = outDir
69
-  }
70
-  if (rootDir) {
71
-    config.rootDir = rootDir
72
-  }
73
-  if (paths) {
74
-    config.paths = paths
75
-  }
76
-  if (extendsPath) {
77
-    const childConfigDirPath = path.dirname(file)
78
-    const parentConfigPath = path.resolve(childConfigDirPath, extendsPath)
79
-    const parentConfigDirPath = path.dirname(parentConfigPath)
80
-    const currentExtension = path.extname(parentConfigPath)
81
-
82
-    let parentExtendedConfigFile = path.format({
83
-      name: parentConfigPath,
84
-      ext: currentExtension === '' ? '.json' : '',
85
-    })
86
-
87
-    /* Ensure without a doubt there's no double extension */
88
-    if (/\.json\.json$/.test(parentExtendedConfigFile)) {
89
-      parentExtendedConfigFile = parentExtendedConfigFile.replace(/\.json\.json$/, '.json')
90
-    }
91
-
92
-    const parentConfig = loadConfig(parentExtendedConfigFile)
93
-
94
-    if (parentConfig.baseUrl) {
95
-      parentConfig.baseUrl = path.resolve(parentConfigDirPath, parentConfig.baseUrl)
96
-    }
97
-
98
-    return {
99
-      ...parentConfig,
100
-      ...config,
101
-    }
102
-  }
103
-
104
-  return config
105
-}
106
-
107
-program
108
-  .version('0.0.1')
109
-  .option('-p, --project <file>', 'path to tsconfig.json')
110
-  .option('-s, --src <path>', 'source root path')
111
-  .option('-o, --out <path>', 'output root path')
112
-  .option('-v, --verbose', 'output logs')
113
-
114
-program.on('--help', () => {
115
-  console.log(`
116
-  $ tscpath -p tsconfig.json
117
-`)
118
-})
119
-
120
-program.parse(process.argv)
121
-
122
-const {
123
-  out: flagOut,
124
-  project = 'tsconfig.json',
125
-  src: flagSrc,
126
-  verbose = false,
127
-} = program as {
128
-  out?: string | undefined
129
-  project?: string
130
-  src?: string | undefined
131
-  verbose?: boolean
132
-}
133
-
134
-const verboseLog = (...args: any[]): void => {
135
-  if (verbose) {
136
-    console.log(...args)
137
-  }
138
-}
139
-
140
-const configFile = resolve(process.cwd(), project)
141
-
142
-const rootDir = resolve(process.cwd())
143
-
144
-verboseLog(`Using tsconfig: ${configFile}`)
145
-
146
-const exitingErr = (): any => {
147
-  throw new Error('--- exiting tsconfig-replace-paths due to parameters missing ---')
148
-}
149
-
150
-const missingConfigErr = (property: string): any => {
151
-  console.error(`Whoops! Please set ${property} in your tsconfig or supply a flag`)
152
-  exitingErr()
153
-}
154
-
155
-const missingDirectoryErr = (directory: string, flag: string): any => {
156
-  console.error(
157
-    `Whoops! ${directory} must be specified in your project => --project ${project}, or flagged with directory => ${flag} './path'`
158
-  )
159
-  exitingErr()
160
-}
161
-
162
-// Imported the TS Config
163
-const returnedTsConfig = loadConfig(configFile)
164
-
165
-// Destructure only the necessary keys, and rename to give context
166
-const {
167
-  baseUrl,
168
-  paths,
169
-  outDir: tsConfigOutDir = '',
170
-  rootDir: tsConfigRootDir = rootDir,
171
-} = returnedTsConfig
172
-
173
-// If no flagSrc or tsConfigRootDir, error
174
-if (!flagSrc && tsConfigRootDir === '') {
175
-  missingConfigErr('compilerOptions.rootDir')
176
-}
177
-
178
-// If no flagOut or tsConfigOutDir, error
179
-if (!flagOut && tsConfigOutDir === '') {
180
-  missingConfigErr('compilerOptions.outDir')
181
-}
182
-
183
-// Are we going to use the flag or ts config for src?
184
-let usingSrcDir: string
185
-if (flagSrc) {
186
-  verboseLog('Using flag --src')
187
-  usingSrcDir = resolve(flagSrc)
188
-} else {
189
-  verboseLog('Using compilerOptions.rootDir from your tsconfig')
190
-  usingSrcDir = resolve(tsConfigRootDir)
191
-}
192
-if (!usingSrcDir) {
193
-  missingDirectoryErr('rootDir', '--src')
194
-}
195
-
196
-// Log which src is being used
197
-verboseLog(`Using src: ${usingSrcDir}`)
198
-
199
-// Are we going to use the flag or ts config for out?
200
-let usingOutDir: string
201
-if (flagOut) {
202
-  verboseLog('Using flag --out')
203
-  usingOutDir = resolve(flagOut)
204
-} else {
205
-  verboseLog('Using compilerOptions.outDir from your tsconfig')
206
-  usingOutDir = resolve(tsConfigOutDir)
207
-}
208
-if (!usingOutDir) {
209
-  missingDirectoryErr('outDir', '--out')
210
-}
211
-
212
-// Log which out is being used
213
-verboseLog(`Using out: ${usingOutDir}`)
214
-
215
-if (!baseUrl) {
216
-  throw new Error('compilerOptions.baseUrl is not set')
217
-}
218
-if (!paths) {
219
-  throw new Error('compilerOptions.paths is not set')
220
-}
221
-if (!usingOutDir) {
222
-  throw new Error('compilerOptions.outDir is not set')
223
-}
224
-if (!usingSrcDir) {
225
-  throw new Error('compilerOptions.rootDir is not set')
226
-}
227
-
228
-verboseLog(`baseUrl: ${baseUrl}`)
229
-verboseLog(`rootDir: ${usingSrcDir}`)
230
-verboseLog(`outDir: ${usingOutDir}`)
231
-verboseLog(`paths: ${JSON.stringify(paths, null, 2)}`)
232
-
233
-const configDir = dirname(configFile)
234
-
235
-const basePath = resolve(configDir, baseUrl)
236
-verboseLog(`basePath: ${basePath}`)
237
-
238
-const outPath = usingOutDir || resolve(basePath, usingOutDir)
239
-verboseLog(`outPath: ${outPath}`)
240
-
241
-const outFileToSrcFile = (x: string): string => resolve(usingSrcDir, relative(outPath, x))
242
-
243
-const aliases = Object.keys(paths)
244
-  .filter((path) => path.startsWith('~'))
245
-  .map((alias) => ({
246
-    prefix: alias.replace(/\*$/, ''),
247
-    aliasPaths: paths[alias as keyof typeof paths].map((p) =>
248
-      resolve(basePath, p.replace(/\*$/, ''))
249
-    ),
250
-  }))
251
-  .filter(({ prefix }) => prefix)
252
-verboseLog(`aliases: ${JSON.stringify(aliases, null, 2)}`)
253
-
254
-const toRelative = (from: string, x: string): string => {
255
-  const rel = relative(from, x)
256
-  return (rel.startsWith('.') ? rel : `./${rel}`).replace(/\\/g, '/')
257
-}
258
-
259
-const exts = ['.js', '.jsx', '.ts', '.tsx', '.d.ts', '.json']
260
-
261
-let replaceCount = 0
262
-
263
-const absToRel = (modulePath: string, outFile: string): string => {
264
-  const alen = aliases.length
265
-
266
-  for (let j = 0; j < alen; j += 1) {
267
-    const { prefix, aliasPaths } = aliases[j]
268
-
269
-    if (modulePath.startsWith(prefix)) {
270
-      const modulePathRel = modulePath.substring(prefix.length)
271
-      const srcFile = outFileToSrcFile(outFile)
272
-      const outRel = relative(basePath, outFile)
273
-
274
-      verboseLog(`${outRel} (source: ${relative(basePath, srcFile)}):`)
275
-      verboseLog(`\timport '${modulePath}'`)
276
-
277
-      const len = aliasPaths.length
278
-      for (let i = 0; i < len; i += 1) {
279
-        const apath = aliasPaths[i]
280
-        const moduleSrc = resolve(apath, modulePathRel)
281
-        if (existsSync(moduleSrc) || exts.some((ext) => existsSync(moduleSrc + ext))) {
282
-          const rel = toRelative(dirname(srcFile), moduleSrc)
283
-
284
-          replaceCount += 1
285
-
286
-          verboseLog(
287
-            `\treplacing '${modulePath}' -> '${rel}' referencing ${relative(basePath, moduleSrc)}`
288
-          )
289
-          return rel
290
-        }
291
-      }
292
-      verboseLog(`\tcould not replace ${modulePath}`)
293
-    }
294
-  }
295
-
296
-  return modulePath
297
-}
298
-
299
-const requireRegex = /(?:import|require)\(['"]([^'"]*)['"]\)/g
300
-const importRegex = /(?:import|from) ['"]([^'"]*)['"]/g
301
-
302
-const replaceImportStatement = (orig: string, matched: string, outFile: string): string => {
303
-  const index = orig.indexOf(matched)
304
-  return (
305
-    orig.substring(0, index) + absToRel(matched, outFile) + orig.substring(index + matched.length)
306
-  )
307
-}
308
-
309
-const replaceAlias = (text: string, outFile: string): string =>
310
-  text
311
-    .replace(requireRegex, (orig, matched) => replaceImportStatement(orig, matched, outFile))
312
-    .replace(importRegex, (orig, matched) => replaceImportStatement(orig, matched, outFile))
313
-
314
-// import relative to absolute path
315
-const files = sync(`${outPath}/**/*.{js,jsx,ts,tsx}`, {
316
-  dot: true,
317
-  noDir: true,
318
-} as any).map((x) => resolve(x))
319
-
320
-let changedFileCount = 0
321
-
322
-const flen = files.length
323
-let count = 0
324
-
325
-for (let i = 0; i < flen; i += 1) {
326
-  const file = files[i]
327
-  const text = readFileSync(file, 'utf8')
328
-  const prevReplaceCount = replaceCount
329
-  const newText = replaceAlias(text, file)
330
-  if (text !== newText) {
331
-    changedFileCount += 1
332
-    verboseLog(`${file}: replaced ${replaceCount - prevReplaceCount} paths`)
333
-    writeFileSync(file, newText, 'utf8')
334
-    count = count + 1
335
-  }
336
-}
337
-
338
-console.log(`Replaced ${replaceCount} paths in ${changedFileCount} files`)

+ 2
- 26
yarn.lock Wyświetl plik

@@ -3513,16 +3513,6 @@
3513 3513
   resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
3514 3514
   integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
3515 3515
 
3516
-"@uqt/ts-path-replace@^1.1.1":
3517
-  version "1.1.1"
3518
-  resolved "https://registry.yarnpkg.com/@uqt/ts-path-replace/-/ts-path-replace-1.1.1.tgz#c753fe92aa55575e7249c9defc908e311e37b93d"
3519
-  integrity sha512-6OBpyAJRDAeneM1m8drI4e7P71kyoACdPVN5OMjsTmzT9becSZz95KVSRRQyljdDSCUPhdGzxOi5iBGp/eBYPQ==
3520
-  dependencies:
3521
-    lodash.merge "^4.6.2"
3522
-    minimist "^1.2.5"
3523
-    node-watch "^0.7.1"
3524
-    replace-in-file "^6.1.0"
3525
-
3526 3516
 "@use-gesture/core@10.1.5":
3527 3517
   version "10.1.5"
3528 3518
   resolved "https://registry.yarnpkg.com/@use-gesture/core/-/core-10.1.5.tgz#4b956bc8aa6354c20c668930c5cacb16fe4415e4"
@@ -4745,7 +4735,7 @@ chalk@^3.0.0:
4745 4735
     ansi-styles "^4.1.0"
4746 4736
     supports-color "^7.1.0"
4747 4737
 
4748
-chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2:
4738
+chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
4749 4739
   version "4.1.2"
4750 4740
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
4751 4741
   integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -10849,11 +10839,6 @@ node-source-walk@^4.0.0, node-source-walk@^4.2.0:
10849 10839
   dependencies:
10850 10840
     "@babel/parser" "^7.0.0"
10851 10841
 
10852
-node-watch@^0.7.1:
10853
-  version "0.7.2"
10854
-  resolved "https://registry.yarnpkg.com/node-watch/-/node-watch-0.7.2.tgz#545f057da8500487eb8287adcb4cb5a7338d7e21"
10855
-  integrity sha512-g53VjSARRv1JdST0LZRIg8RiuLr1TaBbVPsVvxh0/0Ymvi0xYUjDuoqQQAWtHJQUXhiShowPT/aXKNeHBcyQsw==
10856
-
10857 10842
 nodemailer@^6.4.16:
10858 10843
   version "6.7.1"
10859 10844
   resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.1.tgz#09f72f8b375f7b259291757007bcd902c0174c6e"
@@ -12460,15 +12445,6 @@ repeating@^2.0.0:
12460 12445
   dependencies:
12461 12446
     is-finite "^1.0.0"
12462 12447
 
12463
-replace-in-file@^6.1.0:
12464
-  version "6.3.2"
12465
-  resolved "https://registry.yarnpkg.com/replace-in-file/-/replace-in-file-6.3.2.tgz#0f19835137177c89932f45df319f3539a019484f"
12466
-  integrity sha512-Dbt5pXKvFVPL3WAaEB3ZX+95yP0CeAtIPJDwYzHbPP5EAHn+0UoegH/Wg3HKflU9dYBH8UnBC2NvY3P+9EZtTg==
12467
-  dependencies:
12468
-    chalk "^4.1.2"
12469
-    glob "^7.2.0"
12470
-    yargs "^17.2.1"
12471
-
12472 12448
 request@^2.88.0:
12473 12449
   version "2.88.2"
12474 12450
   resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
@@ -15161,7 +15137,7 @@ yargs@^14.2.2:
15161 15137
     y18n "^4.0.0"
15162 15138
     yargs-parser "^15.0.1"
15163 15139
 
15164
-yargs@^17.0.1, yargs@^17.2.1:
15140
+yargs@^17.0.1:
15165 15141
   version "17.2.1"
15166 15142
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.2.1.tgz#e2c95b9796a0e1f7f3bf4427863b42e0418191ea"
15167 15143
   integrity sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==

Ładowanie…
Anuluj
Zapisz