@zuilib/text-editor 0.3.1 → 0.5.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/dist/styles.css CHANGED
@@ -282,12 +282,13 @@
282
282
  border: 1px solid color-mix(in srgb, currentColor 15%, transparent);
283
283
  border-radius: 0.625rem;
284
284
  overflow: hidden;
285
- font-size: 0.875rem;
285
+ /* Density presets set these custom properties inline (tableSettings.ts) */
286
+ font-size: var(--zui-table-font-size, 0.875rem);
286
287
  table-layout: fixed;
287
288
  }
288
289
 
289
290
  .zui-table-cell {
290
- padding: 0.5rem 0.75rem;
291
+ padding: var(--zui-table-cell-padding, 0.5rem 0.75rem);
291
292
  border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent);
292
293
  border-right: 1px solid color-mix(in srgb, currentColor 10%, transparent);
293
294
  vertical-align: top;
@@ -319,13 +320,13 @@
319
320
  }
320
321
 
321
322
  /*
322
- * Floating full-width / content-width toggle, straddling the top-right corner
323
+ * Floating table settings (width, density), straddling the top-right corner
323
324
  * of the table the caret is in (positioned within .zui-text-editor-main). It
324
325
  * stays inside the table's own top margin so it never covers the previous
325
- * paragraph. Content width itself is an inline style on the <table> (see
326
- * tableWidth.ts).
326
+ * paragraph. The settings themselves are an inline style on the <table>
327
+ * (see tableSettings.ts).
327
328
  */
328
- .zui-table-width {
329
+ .zui-table-settings {
329
330
  position: absolute;
330
331
  z-index: 5;
331
332
  display: flex;
@@ -338,7 +339,7 @@
338
339
  transform: translateY(-70%);
339
340
  }
340
341
 
341
- .dark .zui-table-width {
342
+ .dark .zui-table-settings {
342
343
  background: #232329;
343
344
  border-color: rgba(255, 255, 255, 0.1);
344
345
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
@@ -349,17 +350,25 @@
349
350
  margin: 1rem 0;
350
351
  }
351
352
 
352
- .zui-drawing-canvas.is-content-width {
353
- width: fit-content;
354
- max-width: 100%;
355
- }
356
-
357
353
  .zui-drawing-canvas {
354
+ --zui-drawing-accent: #6965db;
358
355
  position: relative;
359
- border-radius: 0.75rem;
356
+ border: 1px solid color-mix(in srgb, currentColor 10%, transparent);
357
+ border-radius: 0.875rem;
360
358
  background: #ffffff;
361
359
  outline: none;
362
360
  overflow: hidden;
361
+ transition: box-shadow 0.18s ease, border-color 0.18s ease;
362
+ }
363
+
364
+ .zui-drawing-canvas.is-editable:focus-within {
365
+ border-color: color-mix(in srgb, var(--zui-drawing-accent) 45%, transparent);
366
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--zui-drawing-accent) 12%, transparent);
367
+ }
368
+
369
+ .zui-drawing-canvas.is-content-width {
370
+ width: fit-content;
371
+ max-width: 100%;
363
372
  }
364
373
 
365
374
  .zui-drawing-surface {
@@ -368,121 +377,242 @@
368
377
  max-width: 100%;
369
378
  touch-action: none;
370
379
  background-color: #ffffff;
371
- background-image: radial-gradient(
372
- circle,
373
- rgba(0, 0, 0, 0.07) 1px,
374
- transparent 1px
375
- );
380
+ background-image: radial-gradient(circle, rgba(0, 0, 0, 0.09) 1px, transparent 1px);
376
381
  background-size: 20px 20px;
377
382
  }
378
383
 
379
- /*
380
- * Dark mode (zui `.dark` class convention). The drawing surface, the color
381
- * swatches, and the inline text input are darkened Excalidraw-style with a
382
- * color-inversion filter, so stored shape colors stay theme-agnostic: the
383
- * default near-black stroke renders light on the dark canvas, and the
384
- * swatches preview exactly what will be drawn.
385
- */
386
- .dark .zui-drawing-canvas {
387
- background: #18181b;
388
- }
389
-
390
- .dark .zui-drawing-surface,
391
- .dark .zui-drawing-swatch,
392
- .dark .zui-drawing-text-input {
393
- filter: invert(93%) hue-rotate(180deg);
384
+ .zui-drawing-shape {
385
+ transition: filter 0.12s ease;
394
386
  }
395
387
 
396
- .dark .zui-drawing-toolbar-group {
397
- background: #232329;
398
- border-color: rgba(255, 255, 255, 0.1);
399
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
388
+ .zui-drawing-canvas.is-editable .zui-drawing-shape:hover:not(.is-selected) {
389
+ filter: drop-shadow(0 0 2.5px color-mix(in srgb, var(--zui-drawing-accent) 55%, transparent));
400
390
  }
401
391
 
402
- .dark .zui-drawing-tool {
403
- color: #e3e3e8;
392
+ .zui-drawing-handle {
393
+ filter: drop-shadow(0 1px 1.5px rgba(0, 0, 0, 0.25));
404
394
  }
405
395
 
406
- .dark .zui-drawing-tool:hover {
407
- background: rgba(255, 255, 255, 0.08);
396
+ .zui-drawing-ghost {
397
+ opacity: 0.55;
398
+ transition: opacity 0.12s ease;
408
399
  }
409
400
 
410
- .dark .zui-drawing-tool.is-active {
411
- background: #45437a;
412
- color: #dcdaff;
401
+ .zui-drawing-ghost:hover {
402
+ opacity: 1;
413
403
  }
414
404
 
415
- .dark .zui-drawing-swatch-label {
416
- color: rgba(255, 255, 255, 0.55);
405
+ .zui-drawing-bind-target {
406
+ animation: zui-drawing-pulse 0.9s ease-in-out infinite alternate;
417
407
  }
418
408
 
419
- .dark .zui-drawing-resize {
420
- background: rgba(255, 255, 255, 0.2);
409
+ @keyframes zui-drawing-pulse {
410
+ from {
411
+ stroke-opacity: 0.55;
412
+ }
413
+ to {
414
+ stroke-opacity: 1;
415
+ }
421
416
  }
422
417
 
423
- .dark .zui-drawing-resize:hover {
424
- background: rgba(255, 255, 255, 0.35);
418
+ @keyframes zui-drawing-pop {
419
+ from {
420
+ opacity: 0;
421
+ transform: translateY(4px) scale(0.98);
422
+ }
423
+ to {
424
+ opacity: 1;
425
+ transform: translateY(0) scale(1);
426
+ }
425
427
  }
426
428
 
429
+ /* Floating toolbar */
427
430
  .zui-drawing-toolbar {
428
431
  position: absolute;
429
- top: 0.5rem;
430
- left: 0.5rem;
431
- right: 0.5rem;
432
+ top: 0.625rem;
433
+ left: 0.625rem;
434
+ right: 0.625rem;
432
435
  z-index: 5;
433
436
  display: flex;
434
437
  flex-wrap: wrap;
435
- align-items: center;
438
+ align-items: flex-start;
436
439
  gap: 0.5rem;
437
440
  pointer-events: none;
438
441
  }
439
442
 
440
- .zui-drawing-toolbar-group {
443
+ .zui-drawing-toolbar-group,
444
+ .zui-drawing-props {
441
445
  display: flex;
442
446
  align-items: center;
443
447
  gap: 0.125rem;
444
448
  padding: 0.25rem;
445
- border: 1px solid rgba(0, 0, 0, 0.08);
446
- border-radius: 0.5rem;
447
- background: #ffffff;
448
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
449
+ border: 1px solid rgba(0, 0, 0, 0.07);
450
+ border-radius: 0.625rem;
451
+ background: rgba(255, 255, 255, 0.88);
452
+ backdrop-filter: blur(10px);
453
+ -webkit-backdrop-filter: blur(10px);
454
+ box-shadow:
455
+ 0 1px 2px rgba(0, 0, 0, 0.05),
456
+ 0 6px 20px -8px rgba(0, 0, 0, 0.18);
449
457
  pointer-events: auto;
450
458
  }
451
459
 
452
- /* Canvas-level controls (width) sit at the far right of the toolbar row */
453
460
  .zui-drawing-toolbar-group-end {
454
461
  margin-left: auto;
455
462
  }
456
463
 
464
+ .zui-drawing-toolbar-divider {
465
+ width: 1px;
466
+ height: 1.125rem;
467
+ margin: 0 0.25rem;
468
+ background: rgba(0, 0, 0, 0.1);
469
+ }
470
+
457
471
  .zui-drawing-tool {
472
+ position: relative;
458
473
  display: inline-flex;
459
474
  align-items: center;
460
475
  justify-content: center;
461
- width: 1.75rem;
462
- height: 1.75rem;
476
+ width: 1.875rem;
477
+ height: 1.875rem;
463
478
  border: none;
464
- border-radius: 0.375rem;
479
+ border-radius: 0.5rem;
465
480
  background: transparent;
466
481
  color: #1e1e1e;
467
482
  cursor: pointer;
483
+ transition:
484
+ background-color 0.12s ease,
485
+ color 0.12s ease,
486
+ transform 0.12s ease;
468
487
  }
469
488
 
470
489
  .zui-drawing-tool:hover {
471
490
  background: rgba(0, 0, 0, 0.06);
472
491
  }
473
492
 
493
+ .zui-drawing-tool:active {
494
+ transform: scale(0.94);
495
+ }
496
+
474
497
  .zui-drawing-tool.is-active {
475
- background: #e0dfff;
498
+ background: color-mix(in srgb, var(--zui-drawing-accent) 16%, transparent);
476
499
  color: #4a47b1;
477
500
  }
478
501
 
502
+ .zui-drawing-tool.is-success {
503
+ color: #2f9e44;
504
+ }
505
+
479
506
  .zui-drawing-tool-danger {
480
507
  color: #e03131;
481
508
  }
482
509
 
510
+ .zui-drawing-tool-danger:hover {
511
+ background: rgba(224, 49, 49, 0.1);
512
+ }
513
+
514
+ .zui-drawing-tool-more {
515
+ width: 2.375rem;
516
+ }
517
+
518
+ .zui-drawing-caret {
519
+ position: absolute;
520
+ right: 0.3rem;
521
+ bottom: 0.3rem;
522
+ width: 0;
523
+ height: 0;
524
+ border-left: 4px solid transparent;
525
+ border-top: 4px solid currentColor;
526
+ opacity: 0.6;
527
+ }
528
+
529
+ .zui-drawing-more {
530
+ position: relative;
531
+ }
532
+
533
+ .zui-drawing-popover {
534
+ position: absolute;
535
+ top: calc(100% + 0.375rem);
536
+ left: 0;
537
+ z-index: 7;
538
+ display: flex;
539
+ flex-direction: column;
540
+ gap: 0.125rem;
541
+ min-width: 9.5rem;
542
+ padding: 0.25rem;
543
+ border: 1px solid rgba(0, 0, 0, 0.08);
544
+ border-radius: 0.625rem;
545
+ background: #ffffff;
546
+ box-shadow:
547
+ 0 1px 2px rgba(0, 0, 0, 0.06),
548
+ 0 10px 28px -8px rgba(0, 0, 0, 0.25);
549
+ animation: zui-drawing-pop 0.14s ease-out;
550
+ transform-origin: top left;
551
+ }
552
+
553
+ .zui-drawing-popover-item {
554
+ display: flex;
555
+ align-items: center;
556
+ gap: 0.5rem;
557
+ padding: 0.375rem 0.5rem;
558
+ border: none;
559
+ border-radius: 0.375rem;
560
+ background: transparent;
561
+ color: #1e1e1e;
562
+ font-size: 0.8125rem;
563
+ text-align: left;
564
+ cursor: pointer;
565
+ transition: background-color 0.12s ease;
566
+ }
567
+
568
+ .zui-drawing-popover-item:hover {
569
+ background: rgba(0, 0, 0, 0.06);
570
+ }
571
+
572
+ .zui-drawing-popover-item.is-active {
573
+ background: color-mix(in srgb, var(--zui-drawing-accent) 16%, transparent);
574
+ color: #4a47b1;
575
+ }
576
+
577
+ /* Contextual property bar: its own row under the tools */
578
+ .zui-drawing-props-host {
579
+ flex-basis: 100%;
580
+ display: flex;
581
+ opacity: 0;
582
+ transform: translateY(-4px);
583
+ pointer-events: none;
584
+ transition:
585
+ opacity 0.16s ease,
586
+ transform 0.16s ease;
587
+ }
588
+
589
+ .zui-drawing-props-host.is-visible {
590
+ opacity: 1;
591
+ transform: translateY(0);
592
+ pointer-events: auto;
593
+ }
594
+
595
+ .zui-drawing-props {
596
+ flex-wrap: wrap;
597
+ gap: 0.25rem;
598
+ padding: 0.25rem 0.375rem;
599
+ }
600
+
601
+ .zui-drawing-props-group {
602
+ display: flex;
603
+ align-items: center;
604
+ gap: 0.125rem;
605
+ }
606
+
607
+ .zui-drawing-props-group + .zui-drawing-props-group {
608
+ padding-left: 0.375rem;
609
+ border-left: 1px solid rgba(0, 0, 0, 0.1);
610
+ }
611
+
483
612
  .zui-drawing-swatch-label {
484
- margin: 0 0.25rem;
613
+ margin: 0 0.25rem 0 0.125rem;
485
614
  font-size: 0.6875rem;
615
+ letter-spacing: 0.02em;
486
616
  color: rgba(0, 0, 0, 0.5);
487
617
  user-select: none;
488
618
  }
@@ -490,58 +620,173 @@
490
620
  .zui-drawing-swatch {
491
621
  width: 1.125rem;
492
622
  height: 1.125rem;
493
- margin: 0 0.0625rem;
494
- border: 1px solid rgba(0, 0, 0, 0.15);
495
- border-radius: 0.3125rem;
496
- cursor: pointer;
623
+ margin: 0 0.125rem;
497
624
  padding: 0;
625
+ border: 1px solid rgba(0, 0, 0, 0.14);
626
+ border-radius: 999px;
627
+ cursor: pointer;
628
+ transition:
629
+ transform 0.12s ease,
630
+ box-shadow 0.12s ease;
631
+ }
632
+
633
+ .zui-drawing-swatch:hover {
634
+ transform: scale(1.12);
498
635
  }
499
636
 
500
637
  .zui-drawing-swatch.is-active {
501
- outline: 2px solid #6965db;
502
- outline-offset: 1px;
638
+ box-shadow:
639
+ 0 0 0 2px #ffffff,
640
+ 0 0 0 3.5px var(--zui-drawing-accent);
503
641
  }
504
642
 
505
643
  .zui-drawing-swatch.is-transparent {
506
644
  background-image: linear-gradient(
507
645
  45deg,
508
- transparent 46%,
509
- #e03131 46%,
510
- #e03131 54%,
511
- transparent 54%
646
+ transparent 44%,
647
+ #e03131 44%,
648
+ #e03131 56%,
649
+ transparent 56%
512
650
  );
513
651
  }
514
652
 
653
+ /* Empty state */
654
+ .zui-drawing-empty {
655
+ position: absolute;
656
+ inset: 0;
657
+ display: flex;
658
+ align-items: center;
659
+ justify-content: center;
660
+ padding-top: 2rem;
661
+ font-size: 0.8125rem;
662
+ color: rgba(0, 0, 0, 0.4);
663
+ pointer-events: none;
664
+ user-select: none;
665
+ }
666
+
667
+ /* Inline text editing */
668
+ .zui-drawing-overlay {
669
+ position: absolute;
670
+ top: 0;
671
+ left: 0;
672
+ right: 0;
673
+ bottom: 0;
674
+ transform-origin: top left;
675
+ pointer-events: none;
676
+ }
677
+
515
678
  .zui-drawing-text-input {
516
679
  position: absolute;
517
680
  z-index: 6;
518
- padding: 2px 4px;
519
- border: 1px dashed #6965db;
520
- border-radius: 0.25rem;
521
- background: rgba(255, 255, 255, 0.9);
522
- font-family: ui-sans-serif, system-ui, sans-serif;
523
- line-height: 1.35;
681
+ margin: 0;
682
+ padding: 0;
683
+ border: 0;
684
+ border-radius: 3px;
685
+ outline: 1.5px dashed var(--zui-drawing-accent, #6965db);
686
+ outline-offset: 3px;
687
+ background: rgba(255, 255, 255, 0.85);
688
+ color: inherit;
524
689
  resize: none;
525
- outline: none;
526
690
  overflow: hidden;
527
- white-space: pre;
691
+ box-sizing: content-box;
692
+ pointer-events: auto;
528
693
  }
529
694
 
695
+ .zui-drawing-text-input::placeholder {
696
+ color: currentColor;
697
+ opacity: 0.35;
698
+ }
699
+
700
+ /* Canvas height grip (bottom-right) */
530
701
  .zui-drawing-resize {
531
702
  position: absolute;
532
- bottom: 0.25rem;
533
- left: 50%;
534
- transform: translateX(-50%);
535
- width: 3rem;
536
- height: 0.375rem;
537
- border-radius: 999px;
538
- background: rgba(0, 0, 0, 0.15);
703
+ right: 0.375rem;
704
+ bottom: 0.375rem;
705
+ display: inline-flex;
706
+ align-items: center;
707
+ justify-content: center;
708
+ width: 1.375rem;
709
+ height: 1.375rem;
710
+ border-radius: 0.375rem;
711
+ color: #1e1e1e;
712
+ opacity: 0.35;
539
713
  cursor: ns-resize;
540
714
  touch-action: none;
715
+ transition:
716
+ opacity 0.12s ease,
717
+ background-color 0.12s ease;
541
718
  }
542
719
 
543
720
  .zui-drawing-resize:hover {
544
- background: rgba(0, 0, 0, 0.3);
721
+ opacity: 0.9;
722
+ background: rgba(0, 0, 0, 0.06);
723
+ }
724
+
725
+ /*
726
+ * Dark mode (zui `.dark` class convention). The drawing surface, the color
727
+ * swatches, and the inline text input are darkened with a color-inversion
728
+ * filter, so stored shape colors stay theme-agnostic: the default
729
+ * near-black stroke renders light on the dark canvas, and the swatches
730
+ * preview exactly what will be drawn.
731
+ */
732
+ .dark .zui-drawing-canvas {
733
+ background: #18181b;
734
+ border-color: rgba(255, 255, 255, 0.1);
735
+ }
736
+
737
+ .dark .zui-drawing-surface,
738
+ .dark .zui-drawing-swatch,
739
+ .dark .zui-drawing-text-input {
740
+ filter: invert(93%) hue-rotate(180deg);
741
+ }
742
+
743
+ .dark .zui-drawing-toolbar-group,
744
+ .dark .zui-drawing-props,
745
+ .dark .zui-drawing-popover {
746
+ background: rgba(35, 35, 41, 0.9);
747
+ border-color: rgba(255, 255, 255, 0.1);
748
+ box-shadow:
749
+ 0 1px 2px rgba(0, 0, 0, 0.3),
750
+ 0 8px 24px -8px rgba(0, 0, 0, 0.6);
751
+ }
752
+
753
+ .dark .zui-drawing-tool,
754
+ .dark .zui-drawing-popover-item,
755
+ .dark .zui-drawing-resize {
756
+ color: #e3e3e8;
757
+ }
758
+
759
+ .dark .zui-drawing-tool:hover,
760
+ .dark .zui-drawing-popover-item:hover,
761
+ .dark .zui-drawing-resize:hover {
762
+ background: rgba(255, 255, 255, 0.08);
763
+ }
764
+
765
+ .dark .zui-drawing-tool.is-active,
766
+ .dark .zui-drawing-popover-item.is-active {
767
+ background: #45437a;
768
+ color: #dcdaff;
769
+ }
770
+
771
+ .dark .zui-drawing-toolbar-divider,
772
+ .dark .zui-drawing-props-group + .zui-drawing-props-group {
773
+ border-color: rgba(255, 255, 255, 0.12);
774
+ background-color: rgba(255, 255, 255, 0.12);
775
+ }
776
+
777
+ .dark .zui-drawing-props-group + .zui-drawing-props-group {
778
+ background-color: transparent;
779
+ }
780
+
781
+ .dark .zui-drawing-swatch-label,
782
+ .dark .zui-drawing-empty {
783
+ color: rgba(255, 255, 255, 0.55);
784
+ }
785
+
786
+ .dark .zui-drawing-swatch.is-active {
787
+ box-shadow:
788
+ 0 0 0 2px #232329,
789
+ 0 0 0 3.5px #8f8bff;
545
790
  }
546
791
 
547
792
  .zui-text-editor-textarea {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zuilib/text-editor",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "ZUI — A markdown editor component wrapping Lexical",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,7 +15,7 @@
15
15
  ],
16
16
  "scripts": {
17
17
  "build": "tsup",
18
- "test": "node tests/roundtrip.mjs"
18
+ "test": "node tests/roundtrip.mjs && node --test tests/*.test.mjs"
19
19
  },
20
20
  "exports": {
21
21
  ".": {