playroom 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/cypress/e2e/keymaps.cy.js +234 -7
- package/cypress/support/utils.js +52 -0
- package/package.json +6 -5
- package/src/Playroom/CodeEditor/CodeEditor.css.ts +99 -1
- package/src/Playroom/CodeEditor/CodeEditor.tsx +24 -0
- package/src/Playroom/CodeEditor/keymaps/comment.ts +20 -1
- package/src/Playroom/SettingsPanel/SettingsPanel.tsx +3 -0
- package/src/Playroom/palettes.ts +5 -3
- package/src/Playroom/sprinkles.css.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# playroom
|
|
2
2
|
|
|
3
|
+
## 0.37.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 94c75f8: Add "Find", "Find and replace", and "Jump to line" functionality.
|
|
8
|
+
|
|
9
|
+
Keybindings for these new commands are:
|
|
10
|
+
|
|
11
|
+
- `Cmd + F` / `Ctrl + F` - Find
|
|
12
|
+
- `Cmd + Option + F` / `Ctrl + Alt + F` - Find and replace
|
|
13
|
+
- `Cmd + G` / `Ctrl + G` - Jump to line
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- 71f694a: Fix issue with "Toggle comment" command commenting certain code outside JSX tags with incorrect syntax.
|
|
18
|
+
|
|
3
19
|
## 0.36.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
typeCode,
|
|
4
4
|
assertCodePaneContains,
|
|
5
5
|
loadPlayroom,
|
|
6
|
+
cmdPlus,
|
|
6
7
|
selectNextWords,
|
|
7
8
|
selectNextLines,
|
|
8
9
|
selectNextCharacters,
|
|
@@ -11,14 +12,13 @@ import {
|
|
|
11
12
|
moveToEndOfLine,
|
|
12
13
|
moveBy,
|
|
13
14
|
moveByWords,
|
|
15
|
+
assertCodePaneSearchMatchesCount,
|
|
16
|
+
findInCode,
|
|
17
|
+
replaceInCode,
|
|
18
|
+
jumpToLine,
|
|
14
19
|
} from '../support/utils';
|
|
15
20
|
import { isMac } from '../../src/utils/formatting';
|
|
16
21
|
|
|
17
|
-
const cmdPlus = (keyCombo) => {
|
|
18
|
-
const platformSpecificKey = isMac() ? 'cmd' : 'ctrl';
|
|
19
|
-
return `${platformSpecificKey}+${keyCombo}`;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
22
|
describe('Keymaps', () => {
|
|
23
23
|
describe('swapLine', () => {
|
|
24
24
|
beforeEach(() => {
|
|
@@ -350,6 +350,163 @@ describe('Keymaps', () => {
|
|
|
350
350
|
});
|
|
351
351
|
});
|
|
352
352
|
|
|
353
|
+
describe('find and replace', () => {
|
|
354
|
+
beforeEach(() => {
|
|
355
|
+
loadPlayroom(`
|
|
356
|
+
<div>First line</div>
|
|
357
|
+
<div>Second line</div>
|
|
358
|
+
<div>Third line</div>
|
|
359
|
+
`);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('should find all occurrences of search term', () => {
|
|
363
|
+
findInCode('div');
|
|
364
|
+
|
|
365
|
+
assertCodePaneSearchMatchesCount(6);
|
|
366
|
+
|
|
367
|
+
cy.focused().type('{esc}');
|
|
368
|
+
|
|
369
|
+
typeCode('c');
|
|
370
|
+
|
|
371
|
+
assertCodePaneContains(dedent`
|
|
372
|
+
<c>First line</div>
|
|
373
|
+
<div>Second line</div>
|
|
374
|
+
<div>Third line</div>
|
|
375
|
+
`);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('should replace and skip occurrences of search term correctly', () => {
|
|
379
|
+
replaceInCode('div', 'span');
|
|
380
|
+
|
|
381
|
+
// replace occurrence
|
|
382
|
+
cy.get('.CodeMirror-dialog button').contains('Yes').click();
|
|
383
|
+
|
|
384
|
+
assertCodePaneContains(dedent`
|
|
385
|
+
<span>First line</div>
|
|
386
|
+
<div>Second line</div>
|
|
387
|
+
<div>Third line</div>
|
|
388
|
+
`);
|
|
389
|
+
|
|
390
|
+
// ignore occurrence
|
|
391
|
+
cy.get('.CodeMirror-dialog button').contains('No').click();
|
|
392
|
+
|
|
393
|
+
assertCodePaneContains(dedent`
|
|
394
|
+
<span>First line</div>
|
|
395
|
+
<div>Second line</div>
|
|
396
|
+
<div>Third line</div>
|
|
397
|
+
`);
|
|
398
|
+
|
|
399
|
+
// replace occurrence
|
|
400
|
+
cy.get('.CodeMirror-dialog button').contains('Yes').click();
|
|
401
|
+
|
|
402
|
+
assertCodePaneContains(dedent`
|
|
403
|
+
<span>First line</div>
|
|
404
|
+
<span>Second line</div>
|
|
405
|
+
<div>Third line</div>
|
|
406
|
+
`);
|
|
407
|
+
|
|
408
|
+
// replace all remaining occurrences
|
|
409
|
+
cy.get('.CodeMirror-dialog button').contains('All').click();
|
|
410
|
+
|
|
411
|
+
assertCodePaneContains(dedent`
|
|
412
|
+
<span>First line</span>
|
|
413
|
+
<span>Second line</span>
|
|
414
|
+
<span>Third line</span>
|
|
415
|
+
`);
|
|
416
|
+
|
|
417
|
+
typeCode('c');
|
|
418
|
+
|
|
419
|
+
assertCodePaneContains(dedent`
|
|
420
|
+
<span>First line</span>
|
|
421
|
+
<span>Second line</spanc>
|
|
422
|
+
<span>Third line</span>
|
|
423
|
+
`);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('should back out of replace correctly', () => {
|
|
427
|
+
replaceInCode('div');
|
|
428
|
+
|
|
429
|
+
typeCode('{esc}');
|
|
430
|
+
|
|
431
|
+
assertCodePaneContains(dedent`
|
|
432
|
+
<div>First line</div>
|
|
433
|
+
<div>Second line</div>
|
|
434
|
+
<div>Third line</div>
|
|
435
|
+
`);
|
|
436
|
+
|
|
437
|
+
typeCode('c');
|
|
438
|
+
|
|
439
|
+
assertCodePaneContains(dedent`
|
|
440
|
+
c<div>First line</div>
|
|
441
|
+
<div>Second line</div>
|
|
442
|
+
<div>Third line</div>
|
|
443
|
+
`);
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
describe('jump to line', () => {
|
|
448
|
+
beforeEach(() => {
|
|
449
|
+
loadPlayroom(`
|
|
450
|
+
<div>First line</div>
|
|
451
|
+
<div>Second line</div>
|
|
452
|
+
<div>Third line</div>
|
|
453
|
+
<div>Forth line</div>
|
|
454
|
+
<div>Fifth line</div>
|
|
455
|
+
<div>Sixth line</div>
|
|
456
|
+
<div>Seventh line</div>
|
|
457
|
+
`);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it('should jump to line number correctly', () => {
|
|
461
|
+
const line = 6;
|
|
462
|
+
jumpToLine(line);
|
|
463
|
+
|
|
464
|
+
typeCode('c');
|
|
465
|
+
|
|
466
|
+
assertCodePaneContains(dedent`
|
|
467
|
+
<div>First line</div>
|
|
468
|
+
<div>Second line</div>
|
|
469
|
+
<div>Third line</div>
|
|
470
|
+
<div>Forth line</div>
|
|
471
|
+
<div>Fifth line</div>
|
|
472
|
+
c<div>Sixth line</div>
|
|
473
|
+
<div>Seventh line</div>
|
|
474
|
+
`);
|
|
475
|
+
|
|
476
|
+
typeCode('{backspace}');
|
|
477
|
+
|
|
478
|
+
const nextLine = 2;
|
|
479
|
+
jumpToLine(nextLine);
|
|
480
|
+
|
|
481
|
+
typeCode('c');
|
|
482
|
+
|
|
483
|
+
assertCodePaneContains(dedent`
|
|
484
|
+
<div>First line</div>
|
|
485
|
+
c<div>Second line</div>
|
|
486
|
+
<div>Third line</div>
|
|
487
|
+
<div>Forth line</div>
|
|
488
|
+
<div>Fifth line</div>
|
|
489
|
+
<div>Sixth line</div>
|
|
490
|
+
<div>Seventh line</div>
|
|
491
|
+
`);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it('should jump to line and column number correctly', () => {
|
|
495
|
+
jumpToLine('6:10');
|
|
496
|
+
typeCode('a');
|
|
497
|
+
|
|
498
|
+
assertCodePaneContains(dedent`
|
|
499
|
+
<div>First line</div>
|
|
500
|
+
<div>Second line</div>
|
|
501
|
+
<div>Third line</div>
|
|
502
|
+
<div>Forth line</div>
|
|
503
|
+
<div>Fifth line</div>
|
|
504
|
+
<div>Sixtha line</div>
|
|
505
|
+
<div>Seventh line</div>
|
|
506
|
+
`);
|
|
507
|
+
});
|
|
508
|
+
});
|
|
509
|
+
|
|
353
510
|
describe('toggleComment', () => {
|
|
354
511
|
const blockStarter = `
|
|
355
512
|
<div>First line</div>
|
|
@@ -412,7 +569,7 @@ describe('Keymaps', () => {
|
|
|
412
569
|
>
|
|
413
570
|
Text
|
|
414
571
|
</button>
|
|
415
|
-
|
|
572
|
+
`);
|
|
416
573
|
|
|
417
574
|
moveBy(0, 2);
|
|
418
575
|
|
|
@@ -429,6 +586,76 @@ describe('Keymaps', () => {
|
|
|
429
586
|
</button>
|
|
430
587
|
`);
|
|
431
588
|
});
|
|
589
|
+
|
|
590
|
+
it('block - expression slot outside tags', () => {
|
|
591
|
+
loadPlayroom(`
|
|
592
|
+
{testFn('test')}
|
|
593
|
+
<div>First line</div>
|
|
594
|
+
<div>Second line</div>
|
|
595
|
+
<div>Third line</div>
|
|
596
|
+
`);
|
|
597
|
+
|
|
598
|
+
executeToggleCommentCommand();
|
|
599
|
+
|
|
600
|
+
assertCodePaneContains(dedent`
|
|
601
|
+
{/* {testFn('test')} */}
|
|
602
|
+
<div>First line</div>
|
|
603
|
+
<div>Second line</div>
|
|
604
|
+
<div>Third line</div>
|
|
605
|
+
`);
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
it('line - inside multi-line expression slot outside tags', () => {
|
|
609
|
+
loadPlayroom(`
|
|
610
|
+
{
|
|
611
|
+
testFn('test')
|
|
612
|
+
}
|
|
613
|
+
<div>First line</div>
|
|
614
|
+
<div>Second line</div>
|
|
615
|
+
<div>Third line</div>
|
|
616
|
+
`);
|
|
617
|
+
|
|
618
|
+
moveBy(0, 1);
|
|
619
|
+
|
|
620
|
+
executeToggleCommentCommand();
|
|
621
|
+
|
|
622
|
+
assertCodePaneContains(dedent`
|
|
623
|
+
{
|
|
624
|
+
// testFn('test')
|
|
625
|
+
}
|
|
626
|
+
<div>First line</div>
|
|
627
|
+
<div>Second line</div>
|
|
628
|
+
<div>Third line</div>
|
|
629
|
+
`);
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
it('line - full line expression slot inside tags', () => {
|
|
633
|
+
loadPlayroom(`
|
|
634
|
+
<div
|
|
635
|
+
prop1="prop1"
|
|
636
|
+
{...props}
|
|
637
|
+
>
|
|
638
|
+
First line
|
|
639
|
+
</div>
|
|
640
|
+
<div>Second line</div>
|
|
641
|
+
<div>Third line</div>
|
|
642
|
+
`);
|
|
643
|
+
|
|
644
|
+
moveBy(0, 2);
|
|
645
|
+
|
|
646
|
+
executeToggleCommentCommand();
|
|
647
|
+
|
|
648
|
+
assertCodePaneContains(dedent`
|
|
649
|
+
<div
|
|
650
|
+
prop1="prop1"
|
|
651
|
+
// {...props}
|
|
652
|
+
>
|
|
653
|
+
First line
|
|
654
|
+
</div>
|
|
655
|
+
<div>Second line</div>
|
|
656
|
+
<div>Third line</div>
|
|
657
|
+
`);
|
|
658
|
+
});
|
|
432
659
|
});
|
|
433
660
|
|
|
434
661
|
describe('should wrap a single line selection in a comment', () => {
|
|
@@ -1391,7 +1618,7 @@ describe('Keymaps', () => {
|
|
|
1391
1618
|
});
|
|
1392
1619
|
});
|
|
1393
1620
|
|
|
1394
|
-
describe('for
|
|
1621
|
+
describe('for a selection beginning during opening comment syntax', () => {
|
|
1395
1622
|
it('block', () => {
|
|
1396
1623
|
loadPlayroom(`
|
|
1397
1624
|
<div>
|
package/cypress/support/utils.js
CHANGED
|
@@ -5,6 +5,11 @@ import dedent from 'dedent';
|
|
|
5
5
|
import { createUrl } from '../../utils';
|
|
6
6
|
import { isMac } from '../../src/utils/formatting';
|
|
7
7
|
|
|
8
|
+
export const cmdPlus = (keyCombo) => {
|
|
9
|
+
const platformSpecificKey = isMac() ? 'cmd' : 'ctrl';
|
|
10
|
+
return `${platformSpecificKey}+${keyCombo}`;
|
|
11
|
+
};
|
|
12
|
+
|
|
8
13
|
const getCodeEditor = () =>
|
|
9
14
|
cy.get('.CodeMirror-code').then((editor) => cy.wrap(editor));
|
|
10
15
|
|
|
@@ -182,3 +187,50 @@ export const loadPlayroom = (initialCode) => {
|
|
|
182
187
|
indexedDB.deleteDatabase(storageKey);
|
|
183
188
|
});
|
|
184
189
|
};
|
|
190
|
+
|
|
191
|
+
const typeInSearchField = (text) =>
|
|
192
|
+
cy.get('.CodeMirror-search-field').type(text);
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @param {string} term
|
|
196
|
+
*/
|
|
197
|
+
export const findInCode = (term) => {
|
|
198
|
+
// Wait necessary to ensure code pane is focussed
|
|
199
|
+
cy.wait(500); // eslint-disable-line @finsit/cypress/no-unnecessary-waiting
|
|
200
|
+
typeCode(`{${cmdPlus('f')}}`);
|
|
201
|
+
|
|
202
|
+
typeInSearchField(`${term}{enter}`);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* @param {string} term
|
|
207
|
+
* @param {string} [replaceWith]
|
|
208
|
+
*/
|
|
209
|
+
export const replaceInCode = (term, replaceWith) => {
|
|
210
|
+
// Wait necessary to ensure code pane is focussed
|
|
211
|
+
cy.wait(500); // eslint-disable-line @finsit/cypress/no-unnecessary-waiting
|
|
212
|
+
typeCode(`{${cmdPlus('alt+f')}}`);
|
|
213
|
+
typeInSearchField(`${term}{enter}`);
|
|
214
|
+
if (replaceWith) {
|
|
215
|
+
typeInSearchField(`${replaceWith}{enter}`);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @param {number} line
|
|
221
|
+
*/
|
|
222
|
+
export const jumpToLine = (line) => {
|
|
223
|
+
// Wait necessary to ensure code pane is focussed
|
|
224
|
+
cy.wait(500); // eslint-disable-line @finsit/cypress/no-unnecessary-waiting
|
|
225
|
+
typeCode(`{${cmdPlus('g')}}`);
|
|
226
|
+
typeCode(`${line}{enter}`);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @param {number} lines
|
|
231
|
+
*/
|
|
232
|
+
export const assertCodePaneSearchMatchesCount = (lines) => {
|
|
233
|
+
getCodeEditor().within(() =>
|
|
234
|
+
cy.get('.cm-searching').should('have.length', lines)
|
|
235
|
+
);
|
|
236
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "playroom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "Design with code, powered by your own component library",
|
|
5
5
|
"main": "utils/index.js",
|
|
6
6
|
"types": "utils/index.d.ts",
|
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
"@octokit/rest": "^19.0.5",
|
|
87
87
|
"@types/jest": "^29.2.4",
|
|
88
88
|
"@types/react-helmet": "^6.1.6",
|
|
89
|
+
"concurrently": "^7.6.0",
|
|
89
90
|
"cypress": "^13.6.6",
|
|
90
91
|
"eslint": "^8.44.0",
|
|
91
92
|
"eslint-config-seek": "^11.3.1",
|
|
@@ -122,11 +123,11 @@
|
|
|
122
123
|
"start:typescript": "./bin/cli.cjs start --config cypress/projects/typescript/playroom.config.js",
|
|
123
124
|
"build:typescript": "./bin/cli.cjs build --config cypress/projects/typescript/playroom.config.js",
|
|
124
125
|
"serve:typescript": "PORT=9002 serve --no-request-logging cypress/projects/typescript/dist",
|
|
125
|
-
"start:all": "
|
|
126
|
-
"build:all": "
|
|
127
|
-
"serve:all": "
|
|
126
|
+
"start:all": "concurrently 'npm:start:*(!all)'",
|
|
127
|
+
"build:all": "concurrently 'npm:build:*(!all)'",
|
|
128
|
+
"serve:all": "concurrently 'npm:serve:*(!all)'",
|
|
128
129
|
"build-and-serve:all": "pnpm build:all && pnpm serve:all",
|
|
129
|
-
"lint": "
|
|
130
|
+
"lint": "concurrently 'npm:lint:*'",
|
|
130
131
|
"lint:eslint": "eslint --cache .",
|
|
131
132
|
"lint:prettier": "prettier --list-different '**/*.{js,md,ts,tsx}'",
|
|
132
133
|
"lint:tsc": "tsc --noEmit",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { style, globalStyle, keyframes } from '@vanilla-extract/css';
|
|
1
|
+
import { style, globalStyle, keyframes, createVar } from '@vanilla-extract/css';
|
|
2
2
|
import { vars, colorPaletteVars, sprinkles } from '../sprinkles.css';
|
|
3
|
+
import { toolbarItemSize } from '../ToolbarItem/ToolbarItem.css';
|
|
3
4
|
|
|
4
5
|
const minimumLineNumberWidth = '50px';
|
|
5
6
|
|
|
@@ -224,3 +225,100 @@ globalStyle('.cm-s-neo .cm-variable', {
|
|
|
224
225
|
globalStyle('.cm-s-neo .cm-number', {
|
|
225
226
|
color: colorPaletteVars.code.number,
|
|
226
227
|
});
|
|
228
|
+
|
|
229
|
+
globalStyle('.CodeMirror-dialog', {
|
|
230
|
+
paddingLeft: vars.space.xlarge,
|
|
231
|
+
paddingRight: vars.space.xlarge,
|
|
232
|
+
minHeight: toolbarItemSize,
|
|
233
|
+
borderBottom: `1px solid ${colorPaletteVars.border.standard}`,
|
|
234
|
+
display: 'flex',
|
|
235
|
+
alignItems: 'center',
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const searchOffset = createVar();
|
|
239
|
+
globalStyle('.CodeMirror-scroll', {
|
|
240
|
+
transform: `translateY(${searchOffset})`,
|
|
241
|
+
transition: vars.transition.fast,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
globalStyle('.dialog-opened .CodeMirror-scroll', {
|
|
245
|
+
vars: {
|
|
246
|
+
[searchOffset]: `${toolbarItemSize}px`,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
globalStyle('.dialog-opened .CodeMirror-lines', {
|
|
251
|
+
paddingBottom: searchOffset,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
globalStyle('.CodeMirror-dialog input', {
|
|
255
|
+
font: vars.font.scale.large,
|
|
256
|
+
fontFamily: vars.font.family.code,
|
|
257
|
+
height: vars.touchableSize,
|
|
258
|
+
flexGrow: 1,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
globalStyle('.CodeMirror-search-hint', {
|
|
262
|
+
display: 'none',
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
globalStyle('.CodeMirror-search-label', {
|
|
266
|
+
display: 'flex',
|
|
267
|
+
alignItems: 'center',
|
|
268
|
+
minHeight: vars.touchableSize,
|
|
269
|
+
font: vars.font.scale.large,
|
|
270
|
+
fontFamily: vars.font.family.code,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
globalStyle('.CodeMirror-search-field', {
|
|
274
|
+
paddingLeft: vars.space.xlarge,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
globalStyle('label.CodeMirror-search-label', {
|
|
278
|
+
flexGrow: 1,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
globalStyle('.dialog-opened.cm-s-neo .CodeMirror-selected', {
|
|
282
|
+
background: colorPaletteVars.background.search,
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
globalStyle('.cm-overlay.cm-searching', {
|
|
286
|
+
paddingTop: 2,
|
|
287
|
+
paddingBottom: 2,
|
|
288
|
+
background: colorPaletteVars.background.selection,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
globalStyle('.CodeMirror-dialog button:first-of-type', {
|
|
292
|
+
marginLeft: vars.space.xlarge,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
globalStyle('.CodeMirror-dialog button', {
|
|
296
|
+
appearance: 'none',
|
|
297
|
+
font: vars.font.scale.standard,
|
|
298
|
+
fontFamily: vars.font.family.standard,
|
|
299
|
+
marginLeft: vars.space.medium,
|
|
300
|
+
paddingTop: vars.space.medium,
|
|
301
|
+
paddingBottom: vars.space.medium,
|
|
302
|
+
paddingLeft: vars.space.large,
|
|
303
|
+
paddingRight: vars.space.large,
|
|
304
|
+
alignSelf: 'center',
|
|
305
|
+
display: 'block',
|
|
306
|
+
background: 'none',
|
|
307
|
+
borderRadius: vars.radii.large,
|
|
308
|
+
cursor: 'pointer',
|
|
309
|
+
border: '1px solid currentColor',
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
globalStyle('.CodeMirror-dialog button:focus', {
|
|
313
|
+
color: colorPaletteVars.foreground.accent,
|
|
314
|
+
boxShadow: colorPaletteVars.shadows.focus,
|
|
315
|
+
outline: 'none',
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
globalStyle('.CodeMirror-dialog button:focus:hover', {
|
|
319
|
+
background: colorPaletteVars.background.selection,
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
globalStyle('.CodeMirror-dialog button:hover', {
|
|
323
|
+
background: colorPaletteVars.background.transparent,
|
|
324
|
+
});
|
|
@@ -2,6 +2,7 @@ import { useRef, useContext, useEffect, useCallback } from 'react';
|
|
|
2
2
|
import { useDebouncedCallback } from 'use-debounce';
|
|
3
3
|
import type { Editor } from 'codemirror';
|
|
4
4
|
import 'codemirror/lib/codemirror.css';
|
|
5
|
+
import 'codemirror/addon/dialog/dialog.css';
|
|
5
6
|
import 'codemirror/theme/neo.css';
|
|
6
7
|
|
|
7
8
|
import {
|
|
@@ -26,6 +27,10 @@ import 'codemirror/addon/edit/closetag';
|
|
|
26
27
|
import 'codemirror/addon/edit/closebrackets';
|
|
27
28
|
import 'codemirror/addon/hint/show-hint';
|
|
28
29
|
import 'codemirror/addon/hint/xml-hint';
|
|
30
|
+
import 'codemirror/addon/dialog/dialog';
|
|
31
|
+
import 'codemirror/addon/search/jump-to-line';
|
|
32
|
+
import 'codemirror/addon/search/search';
|
|
33
|
+
import 'codemirror/addon/search/searchcursor';
|
|
29
34
|
import 'codemirror/addon/selection/active-line';
|
|
30
35
|
import 'codemirror/addon/fold/foldcode';
|
|
31
36
|
import 'codemirror/addon/fold/foldgutter';
|
|
@@ -117,6 +122,17 @@ export const CodeEditor = ({ code, onChange, previewCode, hints }: Props) => {
|
|
|
117
122
|
e.preventDefault();
|
|
118
123
|
dispatch({ type: 'toggleToolbar', payload: { panel: 'snippets' } });
|
|
119
124
|
}
|
|
125
|
+
|
|
126
|
+
// Prevent browser keyboard shortcuts when the search/replace input is focused
|
|
127
|
+
if (
|
|
128
|
+
cmdOrCtrl &&
|
|
129
|
+
document.activeElement?.classList.contains(
|
|
130
|
+
'CodeMirror-search-field'
|
|
131
|
+
) &&
|
|
132
|
+
e.key === 'f'
|
|
133
|
+
) {
|
|
134
|
+
e.preventDefault();
|
|
135
|
+
}
|
|
120
136
|
}
|
|
121
137
|
};
|
|
122
138
|
|
|
@@ -259,6 +275,14 @@ export const CodeEditor = ({ code, onChange, previewCode, hints }: Props) => {
|
|
|
259
275
|
[`${keymapModifierKey}-D`]: selectNextOccurrence,
|
|
260
276
|
[`Shift-${keymapModifierKey}-,`]: wrapInTag,
|
|
261
277
|
[`${keymapModifierKey}-/`]: toggleComment,
|
|
278
|
+
[`${keymapModifierKey}-F`]: 'findPersistent',
|
|
279
|
+
[`${keymapModifierKey}-Alt-F`]: 'replace',
|
|
280
|
+
[`${keymapModifierKey}-G`]: 'jumpToLine',
|
|
281
|
+
['Alt-G']: false, // override default keybinding
|
|
282
|
+
['Alt-F']: false, // override default keybinding
|
|
283
|
+
['Shift-Ctrl-R']: false, // override default keybinding
|
|
284
|
+
['Cmd-Option-F']: false, // override default keybinding
|
|
285
|
+
['Shift-Cmd-Option-F']: false, // override default keybinding
|
|
262
286
|
[`Shift-${keymapModifierKey}-C`]: () => {
|
|
263
287
|
dispatch({
|
|
264
288
|
type: 'copyToClipboard',
|
|
@@ -198,6 +198,19 @@ function getUpdatedContent(existingContent: string, range: TagRange) {
|
|
|
198
198
|
|
|
199
199
|
type CommentType = 'line' | 'block';
|
|
200
200
|
|
|
201
|
+
const isOnlyWhitespace = (input: string) => /^\s+$/.test(input);
|
|
202
|
+
|
|
203
|
+
const isFullExpressionSlot = (tokens: CodeMirror.Token[]) => {
|
|
204
|
+
const formattedLineTokens = tokens.filter(
|
|
205
|
+
(token) => token.type !== 'comment' && !isOnlyWhitespace(token.string)
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
formattedLineTokens.at(0)?.string === '{' &&
|
|
210
|
+
formattedLineTokens.at(-1)?.string === '}'
|
|
211
|
+
);
|
|
212
|
+
};
|
|
213
|
+
|
|
201
214
|
interface TagRange {
|
|
202
215
|
from: CodeMirror.Position;
|
|
203
216
|
to: CodeMirror.Position;
|
|
@@ -219,7 +232,9 @@ const determineCommentType = (
|
|
|
219
232
|
(token) => token.type === 'attribute'
|
|
220
233
|
);
|
|
221
234
|
|
|
222
|
-
const isJavaScriptMode =
|
|
235
|
+
const isJavaScriptMode =
|
|
236
|
+
cm.getModeAt(new Pos(from.line, 0)).name === 'javascript';
|
|
237
|
+
|
|
223
238
|
const isInlineComment = cm
|
|
224
239
|
.getLine(from.line)
|
|
225
240
|
.trimStart()
|
|
@@ -228,6 +243,10 @@ const determineCommentType = (
|
|
|
228
243
|
cm.getLine(from.line).trimStart().startsWith(BLOCK_COMMENT_START) &&
|
|
229
244
|
cm.getLine(to.line).trimEnd().endsWith(BLOCK_COMMENT_END);
|
|
230
245
|
|
|
246
|
+
if (!isBlockComment && isFullExpressionSlot(lineTokens)) {
|
|
247
|
+
return isJavaScriptMode ? 'block' : 'line';
|
|
248
|
+
}
|
|
249
|
+
|
|
231
250
|
if (isInlineComment) {
|
|
232
251
|
return 'line';
|
|
233
252
|
}
|
|
@@ -25,12 +25,15 @@ const getKeyBindings = () => {
|
|
|
25
25
|
const shiftKeySymbol = isMac() ? '⇧' : 'Shift';
|
|
26
26
|
|
|
27
27
|
return {
|
|
28
|
+
Find: [metaKeySymbol, 'F'],
|
|
29
|
+
'Find and replace': [metaKeySymbol, altKeySymbol, 'F'],
|
|
28
30
|
'Toggle comment': [metaKeySymbol, '/'],
|
|
29
31
|
'Wrap selection in tag': [metaKeySymbol, shiftKeySymbol, ','],
|
|
30
32
|
'Format code': [metaKeySymbol, 'S'],
|
|
31
33
|
'Insert snippet': [metaKeySymbol, 'K'],
|
|
32
34
|
'Copy Playroom link': [metaKeySymbol, shiftKeySymbol, 'C'],
|
|
33
35
|
'Select next occurrence': [metaKeySymbol, 'D'],
|
|
36
|
+
'Jump to line number': [metaKeySymbol, 'G'],
|
|
34
37
|
'Swap line up': [altKeySymbol, '↑'],
|
|
35
38
|
'Swap line down': [altKeySymbol, '↓'],
|
|
36
39
|
'Duplicate line up': [shiftKeySymbol, altKeySymbol, '↑'],
|
package/src/Playroom/palettes.ts
CHANGED
|
@@ -49,7 +49,8 @@ export const light = {
|
|
|
49
49
|
neutral: originalPalette.gray2,
|
|
50
50
|
surface: originalPalette.white,
|
|
51
51
|
body: originalPalette.gray1,
|
|
52
|
-
selection: originalPalette.
|
|
52
|
+
selection: transparentize(0.85, originalPalette.blue1),
|
|
53
|
+
search: darken(0.15, originalPalette.blue0),
|
|
53
54
|
},
|
|
54
55
|
border: {
|
|
55
56
|
standard: originalPalette.gray2,
|
|
@@ -143,14 +144,15 @@ export const dark = {
|
|
|
143
144
|
positive: seekPalette.mint[500],
|
|
144
145
|
},
|
|
145
146
|
background: {
|
|
146
|
-
transparent: 'rgba(
|
|
147
|
+
transparent: 'rgba(255, 255, 255, .07)',
|
|
147
148
|
accent: seekPalette.blue[500],
|
|
148
149
|
positive: mix(0.6, seekPalette.grey[900], seekPalette.mint[500]),
|
|
149
150
|
critical: mix(0.7, seekPalette.grey[900], seekPalette.red[600]),
|
|
150
151
|
neutral: seekPalette.grey[800],
|
|
151
152
|
surface: seekPalette.grey[900],
|
|
152
153
|
body: darken(0.03, seekPalette.grey[900]),
|
|
153
|
-
selection: transparentize(0.
|
|
154
|
+
selection: transparentize(0.75, seekPalette.blue[600]),
|
|
155
|
+
search: transparentize(0.25, seekPalette.blue[600]),
|
|
154
156
|
},
|
|
155
157
|
border: {
|
|
156
158
|
standard: seekPalette.grey[800],
|