From 7563575ef56336b5b736805ed0517228fb6a473e Mon Sep 17 00:00:00 2001 From: Proful Sadangi Date: Sat, 27 Nov 2021 04:12:27 +1100 Subject: [PATCH] [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 --- .../TopPanel/StyleMenu/StyleMenu.tsx | 15 +- packages/tldraw/src/state/TldrawApp.ts | 6 +- .../src/state/shapes/DrawUtil/DrawUtil.tsx | 2 + .../shapes/RectangleUtil/RectangleUtil.tsx | 2 + .../state/shapes/StickyUtil/StickyUtil.tsx | 4 + .../src/state/shapes/TextUtil/TextUtil.tsx | 2 + .../src/state/tools/SelectTool/SelectTool.ts | 8 +- replace-paths.ts | 338 ------------------ yarn.lock | 28 +- 9 files changed, 28 insertions(+), 377 deletions(-) delete mode 100644 replace-paths.ts diff --git a/packages/tldraw/src/components/TopPanel/StyleMenu/StyleMenu.tsx b/packages/tldraw/src/components/TopPanel/StyleMenu/StyleMenu.tsx index 211412991..f3714a450 100644 --- a/packages/tldraw/src/components/TopPanel/StyleMenu/StyleMenu.tsx +++ b/packages/tldraw/src/components/TopPanel/StyleMenu/StyleMenu.tsx @@ -2,12 +2,7 @@ import * as React from 'react' import * as DropdownMenu from '@radix-ui/react-dropdown-menu' import { strokes, fills, defaultTextStyle } from '~state/shapes/shared/shape-styles' import { useTldrawApp } from '~hooks' -import { - DMCheckboxItem, - DMContent, - DMRadioItem, - DMTriggerIcon, -} from '~components/Primitives/DropdownMenu' +import { DMCheckboxItem, DMContent, DMRadioItem } from '~components/Primitives/DropdownMenu' import { CircleIcon, DashDashedIcon, @@ -69,9 +64,13 @@ const ALIGN_ICONS = { const themeSelector = (s: TDSnapshot) => (s.settings.isDarkMode ? 'dark' : 'light') const showTextStylesSelector = (s: TDSnapshot) => { - const pageId = s.appState.currentPageId + const { activeTool, currentPageId: pageId } = s.appState const page = s.document.pages[pageId] - return s.document.pageStates[pageId].selectedIds.some((id) => 'text' in page.shapes[id]) + + return ( + activeTool === 'text' || + s.document.pageStates[pageId].selectedIds.some((id) => 'text' in page.shapes[id]) + ) } export const StyleMenu = React.memo(function ColorMenu(): JSX.Element { diff --git a/packages/tldraw/src/state/TldrawApp.ts b/packages/tldraw/src/state/TldrawApp.ts index f0c24a8cd..b630bbc89 100644 --- a/packages/tldraw/src/state/TldrawApp.ts +++ b/packages/tldraw/src/state/TldrawApp.ts @@ -2917,7 +2917,11 @@ export class TldrawApp extends StateManager { this.currentTool.onShapeBlur?.() } - onShapeClone: TLShapeCloneHandler = (info, e) => this.currentTool.onShapeClone?.(info, e) + onShapeClone: TLShapeCloneHandler = (info, e) => { + this.originPoint = this.getPagePoint(info.point) + this.updateInputs(info, e) + this.currentTool.onShapeClone?.(info, e) + } onRenderCountChange = (ids: string[]) => { const appState = this.getAppState() diff --git a/packages/tldraw/src/state/shapes/DrawUtil/DrawUtil.tsx b/packages/tldraw/src/state/shapes/DrawUtil/DrawUtil.tsx index c004d0769..3ebde7129 100644 --- a/packages/tldraw/src/state/shapes/DrawUtil/DrawUtil.tsx +++ b/packages/tldraw/src/state/shapes/DrawUtil/DrawUtil.tsx @@ -31,6 +31,8 @@ export class DrawUtil extends TDShapeUtil { pointCache: Record = {} + canClone = true + getShape = (props: Partial): T => { return Utils.deepMerge( { diff --git a/packages/tldraw/src/state/shapes/RectangleUtil/RectangleUtil.tsx b/packages/tldraw/src/state/shapes/RectangleUtil/RectangleUtil.tsx index 7550d6706..4f52f211d 100644 --- a/packages/tldraw/src/state/shapes/RectangleUtil/RectangleUtil.tsx +++ b/packages/tldraw/src/state/shapes/RectangleUtil/RectangleUtil.tsx @@ -21,6 +21,8 @@ export class RectangleUtil extends TDShapeUtil { canBind = true + canClone = true + getShape = (props: Partial): T => { return Utils.deepMerge( { diff --git a/packages/tldraw/src/state/shapes/StickyUtil/StickyUtil.tsx b/packages/tldraw/src/state/shapes/StickyUtil/StickyUtil.tsx index 1a699ca04..881756002 100644 --- a/packages/tldraw/src/state/shapes/StickyUtil/StickyUtil.tsx +++ b/packages/tldraw/src/state/shapes/StickyUtil/StickyUtil.tsx @@ -22,8 +22,12 @@ export class StickyUtil extends TDShapeUtil { canEdit = true + canClone = true + hideResizeHandles = true + showCloneHandles = true + getShape = (props: Partial): T => { return Utils.deepMerge( { diff --git a/packages/tldraw/src/state/shapes/TextUtil/TextUtil.tsx b/packages/tldraw/src/state/shapes/TextUtil/TextUtil.tsx index 3fd2db865..b24173a82 100644 --- a/packages/tldraw/src/state/shapes/TextUtil/TextUtil.tsx +++ b/packages/tldraw/src/state/shapes/TextUtil/TextUtil.tsx @@ -24,6 +24,8 @@ export class TextUtil extends TDShapeUtil { canBind = true + canClone = true + getShape = (props: Partial): T => { return Utils.deepMerge( { diff --git a/packages/tldraw/src/state/tools/SelectTool/SelectTool.ts b/packages/tldraw/src/state/tools/SelectTool/SelectTool.ts index 1ab1572d3..dddf697f7 100644 --- a/packages/tldraw/src/state/tools/SelectTool/SelectTool.ts +++ b/packages/tldraw/src/state/tools/SelectTool/SelectTool.ts @@ -191,15 +191,15 @@ export class SelectTool extends BaseTool { if (key === 'Tab') { if (this.status === Status.Idle && this.app.selectedIds.length === 1) { const [selectedId] = this.app.selectedIds - const clonedShape = this.getShapeClone(selectedId, 'right') if (clonedShape) { this.app.createShapes(clonedShape) - this.setStatus(Status.Idle) - this.app.setEditingId(clonedShape.id) - this.app.select(clonedShape.id) + if (clonedShape.type === TDShapeType.Sticky) { + this.app.select(clonedShape.id) + this.app.setEditingId(clonedShape.id) + } } } diff --git a/replace-paths.ts b/replace-paths.ts deleted file mode 100644 index afb08434d..000000000 --- a/replace-paths.ts +++ /dev/null @@ -1,338 +0,0 @@ -#! /usr/bin/env node - -import * as program from 'commander' -import { existsSync, readFileSync, writeFileSync } from 'fs' -import { sync } from 'globby' -import { dirname, relative, resolve } from 'path' -import path from 'path' -import fs from 'fs' -import JSON5 from 'json5' - -/* -"baseUrl": ".", -"outDir": "lib", -"paths": { - "src/*": ["src/*"] -}, -*/ - -export interface IRawTSConfig { - extends?: string - compilerOptions?: { - baseUrl?: string - outDir?: string - rootDir?: string - paths?: { [key: string]: string[] } - } -} - -export interface ITSConfig { - baseUrl?: string - outDir?: string - rootDir?: string - compilerOptions?: Record - paths?: { [key: string]: string[] } -} - -export const mapPaths = ( - paths: { [key: string]: string[] }, - mapper: (x: string) => string -): { [key: string]: string[] } => { - const dest = {} as { [key: string]: string[] } - Object.keys(paths).forEach((key) => { - dest[key] = paths[key].map(mapper) - }) - return dest -} - -export const loadConfig = (file: string): ITSConfig => { - const fileToParse = fs.readFileSync(file) - - const parsedJsonFile = JSON5.parse(fileToParse as unknown as string) - - const { - extends: extendsPath, - compilerOptions: { baseUrl, outDir, rootDir, paths } = { - baseUrl: undefined, - outDir: undefined, - rootDir: undefined, - paths: undefined, - }, - } = parsedJsonFile as IRawTSConfig - - const config: ITSConfig = {} - if (baseUrl) { - config.baseUrl = baseUrl - } - if (outDir) { - config.outDir = outDir - } - if (rootDir) { - config.rootDir = rootDir - } - if (paths) { - config.paths = paths - } - if (extendsPath) { - const childConfigDirPath = path.dirname(file) - const parentConfigPath = path.resolve(childConfigDirPath, extendsPath) - const parentConfigDirPath = path.dirname(parentConfigPath) - const currentExtension = path.extname(parentConfigPath) - - let parentExtendedConfigFile = path.format({ - name: parentConfigPath, - ext: currentExtension === '' ? '.json' : '', - }) - - /* Ensure without a doubt there's no double extension */ - if (/\.json\.json$/.test(parentExtendedConfigFile)) { - parentExtendedConfigFile = parentExtendedConfigFile.replace(/\.json\.json$/, '.json') - } - - const parentConfig = loadConfig(parentExtendedConfigFile) - - if (parentConfig.baseUrl) { - parentConfig.baseUrl = path.resolve(parentConfigDirPath, parentConfig.baseUrl) - } - - return { - ...parentConfig, - ...config, - } - } - - return config -} - -program - .version('0.0.1') - .option('-p, --project ', 'path to tsconfig.json') - .option('-s, --src ', 'source root path') - .option('-o, --out ', 'output root path') - .option('-v, --verbose', 'output logs') - -program.on('--help', () => { - console.log(` - $ tscpath -p tsconfig.json -`) -}) - -program.parse(process.argv) - -const { - out: flagOut, - project = 'tsconfig.json', - src: flagSrc, - verbose = false, -} = program as { - out?: string | undefined - project?: string - src?: string | undefined - verbose?: boolean -} - -const verboseLog = (...args: any[]): void => { - if (verbose) { - console.log(...args) - } -} - -const configFile = resolve(process.cwd(), project) - -const rootDir = resolve(process.cwd()) - -verboseLog(`Using tsconfig: ${configFile}`) - -const exitingErr = (): any => { - throw new Error('--- exiting tsconfig-replace-paths due to parameters missing ---') -} - -const missingConfigErr = (property: string): any => { - console.error(`Whoops! Please set ${property} in your tsconfig or supply a flag`) - exitingErr() -} - -const missingDirectoryErr = (directory: string, flag: string): any => { - console.error( - `Whoops! ${directory} must be specified in your project => --project ${project}, or flagged with directory => ${flag} './path'` - ) - exitingErr() -} - -// Imported the TS Config -const returnedTsConfig = loadConfig(configFile) - -// Destructure only the necessary keys, and rename to give context -const { - baseUrl, - paths, - outDir: tsConfigOutDir = '', - rootDir: tsConfigRootDir = rootDir, -} = returnedTsConfig - -// If no flagSrc or tsConfigRootDir, error -if (!flagSrc && tsConfigRootDir === '') { - missingConfigErr('compilerOptions.rootDir') -} - -// If no flagOut or tsConfigOutDir, error -if (!flagOut && tsConfigOutDir === '') { - missingConfigErr('compilerOptions.outDir') -} - -// Are we going to use the flag or ts config for src? -let usingSrcDir: string -if (flagSrc) { - verboseLog('Using flag --src') - usingSrcDir = resolve(flagSrc) -} else { - verboseLog('Using compilerOptions.rootDir from your tsconfig') - usingSrcDir = resolve(tsConfigRootDir) -} -if (!usingSrcDir) { - missingDirectoryErr('rootDir', '--src') -} - -// Log which src is being used -verboseLog(`Using src: ${usingSrcDir}`) - -// Are we going to use the flag or ts config for out? -let usingOutDir: string -if (flagOut) { - verboseLog('Using flag --out') - usingOutDir = resolve(flagOut) -} else { - verboseLog('Using compilerOptions.outDir from your tsconfig') - usingOutDir = resolve(tsConfigOutDir) -} -if (!usingOutDir) { - missingDirectoryErr('outDir', '--out') -} - -// Log which out is being used -verboseLog(`Using out: ${usingOutDir}`) - -if (!baseUrl) { - throw new Error('compilerOptions.baseUrl is not set') -} -if (!paths) { - throw new Error('compilerOptions.paths is not set') -} -if (!usingOutDir) { - throw new Error('compilerOptions.outDir is not set') -} -if (!usingSrcDir) { - throw new Error('compilerOptions.rootDir is not set') -} - -verboseLog(`baseUrl: ${baseUrl}`) -verboseLog(`rootDir: ${usingSrcDir}`) -verboseLog(`outDir: ${usingOutDir}`) -verboseLog(`paths: ${JSON.stringify(paths, null, 2)}`) - -const configDir = dirname(configFile) - -const basePath = resolve(configDir, baseUrl) -verboseLog(`basePath: ${basePath}`) - -const outPath = usingOutDir || resolve(basePath, usingOutDir) -verboseLog(`outPath: ${outPath}`) - -const outFileToSrcFile = (x: string): string => resolve(usingSrcDir, relative(outPath, x)) - -const aliases = Object.keys(paths) - .filter((path) => path.startsWith('~')) - .map((alias) => ({ - prefix: alias.replace(/\*$/, ''), - aliasPaths: paths[alias as keyof typeof paths].map((p) => - resolve(basePath, p.replace(/\*$/, '')) - ), - })) - .filter(({ prefix }) => prefix) -verboseLog(`aliases: ${JSON.stringify(aliases, null, 2)}`) - -const toRelative = (from: string, x: string): string => { - const rel = relative(from, x) - return (rel.startsWith('.') ? rel : `./${rel}`).replace(/\\/g, '/') -} - -const exts = ['.js', '.jsx', '.ts', '.tsx', '.d.ts', '.json'] - -let replaceCount = 0 - -const absToRel = (modulePath: string, outFile: string): string => { - const alen = aliases.length - - for (let j = 0; j < alen; j += 1) { - const { prefix, aliasPaths } = aliases[j] - - if (modulePath.startsWith(prefix)) { - const modulePathRel = modulePath.substring(prefix.length) - const srcFile = outFileToSrcFile(outFile) - const outRel = relative(basePath, outFile) - - verboseLog(`${outRel} (source: ${relative(basePath, srcFile)}):`) - verboseLog(`\timport '${modulePath}'`) - - const len = aliasPaths.length - for (let i = 0; i < len; i += 1) { - const apath = aliasPaths[i] - const moduleSrc = resolve(apath, modulePathRel) - if (existsSync(moduleSrc) || exts.some((ext) => existsSync(moduleSrc + ext))) { - const rel = toRelative(dirname(srcFile), moduleSrc) - - replaceCount += 1 - - verboseLog( - `\treplacing '${modulePath}' -> '${rel}' referencing ${relative(basePath, moduleSrc)}` - ) - return rel - } - } - verboseLog(`\tcould not replace ${modulePath}`) - } - } - - return modulePath -} - -const requireRegex = /(?:import|require)\(['"]([^'"]*)['"]\)/g -const importRegex = /(?:import|from) ['"]([^'"]*)['"]/g - -const replaceImportStatement = (orig: string, matched: string, outFile: string): string => { - const index = orig.indexOf(matched) - return ( - orig.substring(0, index) + absToRel(matched, outFile) + orig.substring(index + matched.length) - ) -} - -const replaceAlias = (text: string, outFile: string): string => - text - .replace(requireRegex, (orig, matched) => replaceImportStatement(orig, matched, outFile)) - .replace(importRegex, (orig, matched) => replaceImportStatement(orig, matched, outFile)) - -// import relative to absolute path -const files = sync(`${outPath}/**/*.{js,jsx,ts,tsx}`, { - dot: true, - noDir: true, -} as any).map((x) => resolve(x)) - -let changedFileCount = 0 - -const flen = files.length -let count = 0 - -for (let i = 0; i < flen; i += 1) { - const file = files[i] - const text = readFileSync(file, 'utf8') - const prevReplaceCount = replaceCount - const newText = replaceAlias(text, file) - if (text !== newText) { - changedFileCount += 1 - verboseLog(`${file}: replaced ${replaceCount - prevReplaceCount} paths`) - writeFileSync(file, newText, 'utf8') - count = count + 1 - } -} - -console.log(`Replaced ${replaceCount} paths in ${changedFileCount} files`) diff --git a/yarn.lock b/yarn.lock index 410236ae9..15596850f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3513,16 +3513,6 @@ resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== -"@uqt/ts-path-replace@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@uqt/ts-path-replace/-/ts-path-replace-1.1.1.tgz#c753fe92aa55575e7249c9defc908e311e37b93d" - integrity sha512-6OBpyAJRDAeneM1m8drI4e7P71kyoACdPVN5OMjsTmzT9becSZz95KVSRRQyljdDSCUPhdGzxOi5iBGp/eBYPQ== - dependencies: - lodash.merge "^4.6.2" - minimist "^1.2.5" - node-watch "^0.7.1" - replace-in-file "^6.1.0" - "@use-gesture/core@10.1.5": version "10.1.5" resolved "https://registry.yarnpkg.com/@use-gesture/core/-/core-10.1.5.tgz#4b956bc8aa6354c20c668930c5cacb16fe4415e4" @@ -4745,7 +4735,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -10849,11 +10839,6 @@ node-source-walk@^4.0.0, node-source-walk@^4.2.0: dependencies: "@babel/parser" "^7.0.0" -node-watch@^0.7.1: - version "0.7.2" - resolved "https://registry.yarnpkg.com/node-watch/-/node-watch-0.7.2.tgz#545f057da8500487eb8287adcb4cb5a7338d7e21" - integrity sha512-g53VjSARRv1JdST0LZRIg8RiuLr1TaBbVPsVvxh0/0Ymvi0xYUjDuoqQQAWtHJQUXhiShowPT/aXKNeHBcyQsw== - nodemailer@^6.4.16: version "6.7.1" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.1.tgz#09f72f8b375f7b259291757007bcd902c0174c6e" @@ -12460,15 +12445,6 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -replace-in-file@^6.1.0: - version "6.3.2" - resolved "https://registry.yarnpkg.com/replace-in-file/-/replace-in-file-6.3.2.tgz#0f19835137177c89932f45df319f3539a019484f" - integrity sha512-Dbt5pXKvFVPL3WAaEB3ZX+95yP0CeAtIPJDwYzHbPP5EAHn+0UoegH/Wg3HKflU9dYBH8UnBC2NvY3P+9EZtTg== - dependencies: - chalk "^4.1.2" - glob "^7.2.0" - yargs "^17.2.1" - request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" @@ -15161,7 +15137,7 @@ yargs@^14.2.2: y18n "^4.0.0" yargs-parser "^15.0.1" -yargs@^17.0.1, yargs@^17.2.1: +yargs@^17.0.1: version "17.2.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.2.1.tgz#e2c95b9796a0e1f7f3bf4427863b42e0418191ea" integrity sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==