@37signals/lexxy 0.8.0-beta → 0.8.2-beta

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.
@@ -87,7 +87,6 @@
87
87
  object {
88
88
  inline-size: auto;
89
89
  margin-inline: auto;
90
- min-block-size: 3em;
91
90
  max-block-size: 32rem;
92
91
  object-fit: contain;
93
92
 
@@ -119,7 +118,7 @@
119
118
  }
120
119
  }
121
120
 
122
- ol, ul {
121
+ ol, ul:not(.lexxy-prompt-menu) {
123
122
  margin-inline-start: calc(var(--lexxy-content-margin) * 1.5);
124
123
  padding: 0;
125
124
  }
@@ -260,7 +259,7 @@
260
259
  padding: 1ch;
261
260
  text-align: start;
262
261
  word-break: normal;
263
-
262
+
264
263
  *:last-child {
265
264
  margin-block-end: 0;
266
265
  }
@@ -288,11 +287,11 @@
288
287
 
289
288
  .attachment {
290
289
  --lexxy-attachment-gap: 0.75ch;
291
-
290
+
292
291
  block-size: auto;
293
292
  box-sizing: border-box;
294
293
  border-radius: var(--lexxy-radius);
295
- display: table; /* Using table to wrap the caption to fit the attachment's width */
294
+ display: block;
296
295
  inline-size: fit-content;
297
296
  margin-inline: auto;
298
297
  max-inline-size: 100%;
@@ -300,26 +299,21 @@
300
299
  padding: var(--lexxy-attachment-gap);
301
300
  position: relative;
302
301
  text-align: center;
303
-
304
- :where(progress) {
305
- inline-size: 100%;
306
- margin: auto;
307
- }
308
302
  }
309
303
 
310
304
  .attachment__caption {
311
- caption-side: bottom;
312
305
  color: var(--lexxy-color-text-subtle);
313
- display: table-caption;
306
+ display: block;
314
307
  font-size: var(--lexxy-text-small);
308
+ padding: 1ch;
315
309
  }
316
310
 
317
311
  .attachment__icon {
318
312
  aspect-ratio: 4/5;
319
313
  background-color: color-mix(var(--lexxy-attachment-icon-color), transparent 90%);
320
314
  block-size: 3lh;
321
- border: 2px solid var(--lexxy-attachment-icon-color);
322
- border-block-start-width: 1ch;
315
+ border: 1px solid var(--lexxy-attachment-icon-color);
316
+ border-block-start-width: 1.5ch;
323
317
  border-radius: var(--lexxy-radius);
324
318
  box-sizing: border-box;
325
319
  color: var(--lexxy-attachment-icon-color);
@@ -327,12 +321,13 @@
327
321
  font-size: var(--lexxy-text-small);
328
322
  font-weight: bold;
329
323
  inline-size: auto;
324
+ overflow: hidden;
330
325
  place-content: center;
331
326
  text-transform: uppercase;
327
+ white-space: nowrap;
332
328
  }
333
329
 
334
330
  .attachment--preview {
335
-
336
331
  img, video {
337
332
  border-radius: var(--lexxy-radius);
338
333
  block-size: auto;
@@ -345,6 +340,10 @@
345
340
  > a {
346
341
  display: block;
347
342
  }
343
+
344
+ .attachment__caption {
345
+ padding-block-end: 0.5ch;
346
+ }
348
347
  }
349
348
 
350
349
  .attachment--file {
@@ -353,7 +352,6 @@
353
352
  align-items: center;
354
353
  display: flex;
355
354
  flex-wrap: wrap;
356
- gap: 0;
357
355
  inline-size: auto;
358
356
 
359
357
  .attachment__caption {
@@ -408,24 +406,39 @@
408
406
  --lexxy-attachment-icon-color: var(--lexxy-color-green);
409
407
  }
410
408
 
409
+ .attachment--error {
410
+ padding-block: 2ch;
411
+ }
412
+
411
413
 
412
414
  /* Image galleries */
413
415
  /* ------------------------------------------------------------------------ */
414
416
 
415
417
  .attachment-gallery {
416
- display: grid;
417
- grid-template-columns: repeat(3, minmax(0, 1fr));
418
+ display: block;
419
+ text-align: center;
418
420
 
419
421
  .attachment {
420
- display: flex;
421
- flex-direction: column;
422
- gap: 1ch;
423
- inline-size: 100%;
422
+ display: inline-block;
423
+ inline-size: calc(33.333% - 0.8ch);
424
+ vertical-align: top;
425
+
426
+ img {
427
+ margin: auto;
428
+ max-block-size: 50rem;
429
+ object-fit: contain;
430
+ }
431
+
432
+ .attachment__caption {
433
+ padding-block-end: 1ch;
434
+ }
424
435
  }
425
436
 
426
- &:has(> :nth-child(2):last-child),
427
- &:has(> :nth-child(4):last-child) {
428
- grid-template-columns: repeat(2, minmax(0, 1fr));
437
+ &.attachment-gallery--2,
438
+ &.attachment-gallery--4 {
439
+ .attachment {
440
+ inline-size: calc(50% - 0.8ch);
441
+ }
429
442
  }
430
443
  }
431
444
 
@@ -438,13 +451,10 @@
438
451
  --lexxy-attachment-image-size: 1em;
439
452
  --lexxy-attachment-text-color: currentColor;
440
453
 
441
- align-items: center;
442
454
  background: var(--lexxy-attachment-bg-color);
443
455
  border-radius: var(--lexxy-radius);
444
- box-shadow: -0.25ch 0 0 var(--lexxy-attachment-bg-color), 0.5ch 0 0 var(--lexxy-attachment-bg-color);
445
456
  color: var(--lexxy-attachment-text-color);
446
- display: inline-flex;
447
- gap: 0.25ch;
457
+ display: inline;
448
458
  margin: 0;
449
459
  padding: 0;
450
460
  position: relative;
@@ -454,11 +464,8 @@
454
464
  block-size: var(--lexxy-attachment-image-size);
455
465
  border-radius: 50%;
456
466
  inline-size: var(--lexxy-attachment-image-size);
457
- }
458
-
459
- &.node--selected {
460
- --lexxy-attachment-bg-color: var(--lexxy-color-accent-dark);
461
- --lexxy-attachment-text-color: var(--lexxy-color-ink-inverted);
467
+ margin-inline-end: 0.25ch;
468
+ vertical-align: middle;
462
469
  }
463
470
  }
464
471
 
@@ -478,5 +485,4 @@
478
485
  margin: 0;
479
486
  }
480
487
  }
481
-
482
488
  }
@@ -76,7 +76,7 @@
76
76
  position: relative;
77
77
  transition: all 0.1s ease-in-out;
78
78
 
79
- /* Used for column and row adding visualisations */
79
+ /* Used for column and row adding visualizations */
80
80
  &:after {
81
81
  box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent inset;
82
82
  content: "";
@@ -185,16 +185,46 @@
185
185
  cursor: pointer;
186
186
  }
187
187
 
188
+ .attachment {
189
+ progress {
190
+ max-inline-size: 10ch;
191
+ margin: auto;
192
+ }
193
+
194
+ &.attachment--preview {
195
+ progress {
196
+ inset-block-start: 2ch;
197
+ inset-inline-start: 0;
198
+ inset-inline-end: 0;
199
+ position: absolute;
200
+ }
201
+
202
+ lexxy-node-delete-button {
203
+ inset-block-start: 1.5ch;
204
+ inset-inline-end: 1.5ch;
205
+ inset-inline-start: unset;
206
+ }
207
+ }
208
+ }
209
+
188
210
  .attachment:hover:not(.node--selected) {
189
211
  outline: var(--lexxy-focus-ring-size) solid color-mix(in oklch, var(--lexxy-focus-ring-color) 30%, transparent);
190
212
  outline-offset: var(--lexxy-focus-ring-offset);
191
213
  }
192
214
 
215
+ .attachment--file {
216
+ lexxy-node-delete-button {
217
+ inset-block-start: 50%;
218
+ inset-inline-end: 1ch;
219
+ inset-inline-start: unset;
220
+ transform: translate(0, -50%);
221
+ }
222
+ }
223
+
193
224
  /* Image galleries */
194
225
  /* ------------------------------------------------------------------------ */
195
226
 
196
227
  .attachment-gallery {
197
- --lexxy-attachment-gallery-columns: 3;
198
228
  --lexxy-attachment-gallery-gap: 0.4ch;
199
229
  --lexxy-focus-ring-offset: -6px;
200
230
 
@@ -202,43 +232,24 @@
202
232
  padding: 0;
203
233
 
204
234
  .attachment {
205
- box-sizing: border-box;
206
- display: inline-flex;
207
- flex-direction: column;
208
- gap: 0;
209
- inline-size: calc(100% / var(--lexxy-attachment-gallery-columns) - var(--lexxy-attachment-gallery-gap) * 2);
210
235
  margin: var(--lexxy-attachment-gallery-gap);
211
236
  padding: 0;
212
237
  padding-block-end: var(--lexxy-attachment-gap);
213
238
  vertical-align: top;
214
239
 
215
- .attachment__container {
216
- display: block;
217
- margin: 0;
218
- padding: 1ch;
219
- padding-block-end: 0;
240
+ &.attachment--error {
241
+ padding: 2ch;
220
242
  }
221
243
 
222
244
  img {
223
- block-size: auto;
224
245
  box-sizing: border-box;
225
- inline-size: auto;
226
- margin: auto;
227
- max-block-size: 50rem;
228
- max-inline-size: 100%;
229
- object-fit: contain;
246
+ padding: 1ch;
247
+ padding-block-end: 0;
230
248
  }
231
249
  }
232
-
233
- &:has(> .attachment:nth-child(2):last-of-type),
234
- &:has(> .attachment:nth-child(4):last-of-type) {
235
- --lexxy-attachment-gallery-columns: 2;
236
- }
237
250
  }
238
251
 
239
252
  .attachment__caption {
240
- padding: 0 1ch;
241
-
242
253
  textarea {
243
254
  background: transparent;
244
255
  block-size: fit-content;
@@ -251,9 +262,9 @@
251
262
  font-family: inherit;
252
263
  inline-size: 100%;
253
264
  max-inline-size: 100%;
265
+ padding: 0;
254
266
  resize: none;
255
267
  text-align: center;
256
- padding: 1ch 0;
257
268
 
258
269
  &:focus {
259
270
  background: var(--lexxy-color-canvas);
@@ -276,7 +287,7 @@
276
287
 
277
288
  /* Placeholder */
278
289
  :where(.lexxy-editor--empty) {
279
- .lexxy-editor__content:not(:has(ul, ol))::before {
290
+ .lexxy-editor__content:not(:has(h1, h2, h3, h4, h5, h6, table, ul, ol, figure, .attachment-gallery))::before {
280
291
  content: attr(placeholder);
281
292
  color: currentColor;
282
293
  cursor: text;
@@ -331,7 +342,7 @@
331
342
  &[data-attachments="false"] button[name="upload"]{
332
343
  display: none;
333
344
  }
334
-
345
+
335
346
  .lexxy-editor__toolbar-button {
336
347
  aspect-ratio: 1;
337
348
  block-size: var(--lexxy-toolbar-button-size);
@@ -583,7 +594,7 @@
583
594
  /* Floating controls */
584
595
  :where(.lexxy-floating-controls) {
585
596
  --button-size: 2.3lh;
586
- --table-tools-radius: calc(var(--lexxy-radius) * 1.5);
597
+ --floating-tools-radius: calc(var(--lexxy-radius) * 1.5);
587
598
 
588
599
  color: var(--lexxy-color-ink-inverted);
589
600
  font-size: var(--lexxy-text-small);
@@ -599,7 +610,7 @@
599
610
  color: var(--lexxy-color-ink-inverted);
600
611
  display: flex;
601
612
  justify-content: center;
602
- line-height: 1;
613
+ line-height: inherit;
603
614
  min-block-size: var(--button-size);
604
615
  min-inline-size: var(--button-size);
605
616
  user-select: none;
@@ -645,7 +656,7 @@
645
656
 
646
657
  .lexxy-floating-controls__group {
647
658
  background-color: var(--lexxy-color-ink);
648
- border-radius: var(--table-tools-radius);
659
+ border-radius: var(--floating-tools-radius);
649
660
  padding: 0.25ch;
650
661
  }
651
662
 
@@ -672,7 +683,7 @@
672
683
 
673
684
  .lexxy-table-control__more-menu-details {
674
685
  background: var(--lexxy-color-ink);
675
- border-radius: var(--table-tools-radius);
686
+ border-radius: var(--floating-tools-radius);
676
687
  display: flex;
677
688
  flex-direction: column;
678
689
  gap: 0.25ch;
@@ -721,7 +732,7 @@
721
732
  span {
722
733
  align-items: center;
723
734
  background: var(--lexxy-color-ink);
724
- border-radius: var(--table-tools-radius);
735
+ border-radius: var(--floating-tools-radius);
725
736
  color: color-mix(in srgb, var(--lexxy-color-ink-inverted) 80%, transparent);
726
737
  display: flex;
727
738
  inset-inline-end: calc(var(--button-size) + 1ch);
@@ -755,11 +766,10 @@
755
766
  /* Attachment delete button */
756
767
 
757
768
  &:is(lexxy-node-delete-button) {
758
- inset-block-start: 0.5ch;
759
- inset-inline-end: 0.5ch;
769
+ inset-block-start: 0;
770
+ inset-inline-start: 100%;
760
771
  opacity: 0;
761
772
  pointer-events: none;
762
- transition: opacity 0.1s ease-in-out;
763
773
 
764
774
  .node--selected & {
765
775
  opacity: 1;
@@ -810,9 +820,11 @@
810
820
 
811
821
  :where(.lexxy-prompt-menu) {
812
822
  --lexxy-prompt-avatar-size: 24px;
813
- --lexxy-prompt-min-width: 20ch;
814
823
  --lexxy-prompt-padding: 0.5ch;
815
824
 
825
+ --lexxy-prompt-offset-x: 0;
826
+ --lexxy-prompt-offset-y: 0;
827
+
816
828
  background-color: var(--lexxy-color-canvas);
817
829
  border-radius: calc(var(--lexxy-prompt-padding) * 2);
818
830
  box-shadow: var(--lexxy-shadow);
@@ -820,13 +832,27 @@
820
832
  font-family: var(--lexxy-font-base);
821
833
  font-size: var(--lexxy-text-small);
822
834
  list-style: none;
835
+ inset-block-start: var(--lexxy-prompt-offset-y);
836
+ inset-inline-start: var(--lexxy-prompt-offset-x);
823
837
  margin: 0;
824
838
  max-block-size: 200px;
825
- min-inline-size: var(--lexxy-prompt-min-width);
839
+ max-inline-size: min(20ch, calc(100% - var(--lexxy-prompt-offset-x)));
840
+ min-inline-size: 20ch;
826
841
  overflow: auto;
827
842
  padding: var(--lexxy-prompt-padding);
843
+ position: absolute;
828
844
  visibility: hidden;
829
845
  z-index: var(--lexxy-z-popup);
846
+
847
+ &[data-clipped-at-right] {
848
+ inset-inline-start: unset;
849
+ inset-inline-end: 1ch;
850
+ }
851
+
852
+ &[data-clipped-at-bottom] {
853
+ inset-block-start: unset;
854
+ inset-block-end: var(--lexxy-prompt-offset-y);
855
+ }
830
856
  }
831
857
 
832
858
  :where(.lexxy-prompt-menu--visible) {
@@ -840,7 +866,6 @@
840
866
  display: flex;
841
867
  gap: var(--lexxy-prompt-padding);
842
868
  padding: var(--lexxy-prompt-padding);
843
- white-space: nowrap;
844
869
 
845
870
  &:hover {
846
871
  background-color: var(--lexxy-color-ink-lightest);
@@ -867,3 +892,36 @@
867
892
  color: var(--lexxy-color-ink-medium);
868
893
  padding: var(--lexxy-prompt-padding);
869
894
  }
895
+
896
+
897
+ /* --------------------------------------------------------------------------
898
+ /* Custom attachments */
899
+ action-text-attachment[content-type^="application/vnd.actiontext"] {
900
+ &.node--selected {
901
+ --lexxy-attachment-bg-color: var(--lexxy-color-accent-dark);
902
+ --lexxy-attachment-text-color: var(--lexxy-color-ink-inverted);
903
+
904
+ outline: none;
905
+ }
906
+
907
+ lexxy-node-delete-button {
908
+ display: none;
909
+ inset-inline-start: 0;
910
+ line-height: 1lh;
911
+
912
+ .lexxy-floating-controls__group {
913
+ --button-size: 1lh;
914
+
915
+ background-color: var(--lexxy-color-accent-dark);
916
+ padding: 0;
917
+
918
+ button {
919
+ border-radius: 50%;
920
+ }
921
+ }
922
+ }
923
+
924
+ &.node--selected lexxy-node-delete-button {
925
+ display: block;
926
+ }
927
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@37signals/lexxy",
3
- "version": "0.8.0-beta",
3
+ "version": "0.8.2-beta",
4
4
  "description": "Lexxy - A modern rich text editor for Rails.",
5
5
  "module": "dist/lexxy.esm.js",
6
6
  "type": "module",
@@ -16,6 +16,8 @@
16
16
  "license": "MIT",
17
17
  "devDependencies": {
18
18
  "@eslint/js": "^9.15.0",
19
+ "@playwright/test": "^1.52.0",
20
+ "@rails/activestorage": "^8.1.200",
19
21
  "@rollup/plugin-commonjs": "^29.0.0",
20
22
  "@rollup/plugin-inject": "^5.0.5",
21
23
  "@rollup/plugin-node-resolve": "^16.0.1",
@@ -25,33 +27,42 @@
25
27
  "rollup": "^4.44.1",
26
28
  "rollup-plugin-copy": "^3.5.0",
27
29
  "rollup-plugin-gzip": "^4.1.1",
30
+ "vite": "^7.3.1",
28
31
  "vitest": "^4.0.16"
29
32
  },
30
33
  "scripts": {
31
34
  "build": "rollup -c",
32
35
  "build:npm": "rollup -c rollup.config.npm.mjs",
36
+ "benchmark:browser": "node scripts/benchmarks/run-browser-benchmarks.js",
37
+ "benchmark:browser:compare": "node scripts/benchmarks/compare-browser-benchmarks.js",
33
38
  "watch": "rollup -wc --watch.onEnd=\"rails restart\"",
34
39
  "lint": "eslint",
35
- "test": "vitest --environment=jsdom",
40
+ "test": "vitest",
41
+ "test:browser": "npx playwright test --config test/browser/playwright.config.js",
42
+ "test:browser:chromium": "npx playwright test --config test/browser/playwright.config.js --project=chromium",
43
+ "test:browser:firefox": "npx playwright test --config test/browser/playwright.config.js --project=firefox",
44
+ "test:browser:webkit": "npx playwright test --config test/browser/playwright.config.js --project=webkit",
45
+ "test:browser:headed": "npx playwright test --config test/browser/playwright.config.js --headed",
46
+ "test:browser:debug": "npx playwright test --config test/browser/playwright.config.js --debug",
36
47
  "prerelease": "yarn build:npm",
37
48
  "release": "yarn build:npm && yarn publish"
38
49
  },
39
50
  "dependencies": {
40
- "@lexical/extension": "^0.38.2",
41
- "@lexical/clipboard": "^0.38.2",
42
- "@lexical/code": "^0.38.2",
43
- "@lexical/history": "^0.38.2",
44
- "@lexical/html": "^0.38.2",
45
- "@lexical/link": "^0.38.2",
46
- "@lexical/list": "^0.38.2",
47
- "@lexical/markdown": "^0.38.2",
48
- "@lexical/plain-text": "^0.38.2",
49
- "@lexical/rich-text": "^0.38.2",
50
- "@lexical/selection": "^0.38.2",
51
- "@lexical/table": "^0.38.2",
52
- "@lexical/utils": "^0.38.2",
53
- "lexical": "^0.38.2",
51
+ "@lexical/clipboard": "^0.41.0",
52
+ "@lexical/code": "^0.41.0",
53
+ "@lexical/extension": "^0.41.0",
54
+ "@lexical/history": "^0.41.0",
55
+ "@lexical/html": "^0.41.0",
56
+ "@lexical/link": "^0.41.0",
57
+ "@lexical/list": "^0.41.0",
58
+ "@lexical/markdown": "^0.41.0",
59
+ "@lexical/plain-text": "^0.41.0",
60
+ "@lexical/rich-text": "^0.41.0",
61
+ "@lexical/selection": "^0.41.0",
62
+ "@lexical/table": "^0.41.0",
63
+ "@lexical/utils": "^0.41.0",
54
64
  "dompurify": "^3.3.0",
65
+ "lexical": "^0.41.0",
55
66
  "marked": "^16.4.1",
56
67
  "prismjs": "^1.30.0"
57
68
  },