@nuasite/collections-admin 0.43.0-beta.1 → 0.43.0-beta.2

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/src/styles.css CHANGED
@@ -16,6 +16,7 @@
16
16
  --nua-cadmin-danger: #b91c1c;
17
17
  --nua-cadmin-danger-bg: #fef2f2;
18
18
 
19
+ position: relative;
19
20
  display: flex;
20
21
  flex-direction: column;
21
22
  height: 100%;
@@ -369,3 +370,383 @@
369
370
  border: 1px solid var(--nua-cadmin-border);
370
371
  display: block;
371
372
  }
373
+
374
+ /* --- Editor: form controls --- */
375
+
376
+ .nua-cadmin-input,
377
+ .nua-cadmin-textarea,
378
+ .nua-cadmin-body-editor,
379
+ select.nua-cadmin-input {
380
+ width: 100%;
381
+ padding: 7px 10px;
382
+ border: 1px solid var(--nua-cadmin-border);
383
+ border-radius: 6px;
384
+ background: var(--nua-cadmin-bg);
385
+ color: var(--nua-cadmin-fg);
386
+ font-family: inherit;
387
+ font-size: 13px;
388
+ line-height: 1.5;
389
+ }
390
+
391
+ .nua-cadmin-input:focus,
392
+ .nua-cadmin-textarea:focus,
393
+ .nua-cadmin-body-editor:focus,
394
+ select.nua-cadmin-input:focus {
395
+ outline: none;
396
+ border-color: var(--nua-cadmin-accent);
397
+ box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.15);
398
+ }
399
+
400
+ .nua-cadmin-textarea,
401
+ .nua-cadmin-body-editor {
402
+ resize: vertical;
403
+ min-height: 64px;
404
+ }
405
+
406
+ .nua-cadmin-body-editor {
407
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
408
+ font-size: 12px;
409
+ }
410
+
411
+ .nua-cadmin-field-loading {
412
+ padding: 6px 0;
413
+ color: var(--nua-cadmin-muted);
414
+ font-style: italic;
415
+ }
416
+
417
+ /* --- Boolean toggle --- */
418
+
419
+ .nua-cadmin-toggle {
420
+ display: inline-flex;
421
+ align-items: center;
422
+ gap: 8px;
423
+ padding: 4px;
424
+ padding-right: 10px;
425
+ border: 1px solid var(--nua-cadmin-border);
426
+ border-radius: 999px;
427
+ background: var(--nua-cadmin-bg);
428
+ cursor: pointer;
429
+ color: var(--nua-cadmin-muted);
430
+ font-size: 12px;
431
+ }
432
+
433
+ .nua-cadmin-toggle-knob {
434
+ width: 22px;
435
+ height: 22px;
436
+ border-radius: 50%;
437
+ background: var(--nua-cadmin-border);
438
+ transition: background 0.15s, transform 0.15s;
439
+ }
440
+
441
+ .nua-cadmin-toggle-on {
442
+ border-color: var(--nua-cadmin-accent);
443
+ color: var(--nua-cadmin-accent);
444
+ }
445
+
446
+ .nua-cadmin-toggle-on .nua-cadmin-toggle-knob {
447
+ background: var(--nua-cadmin-accent);
448
+ transform: translateX(2px);
449
+ }
450
+
451
+ .nua-cadmin-toggle-publish {
452
+ font-weight: 600;
453
+ }
454
+
455
+ /* --- Array repeater --- */
456
+
457
+ .nua-cadmin-array {
458
+ display: flex;
459
+ flex-direction: column;
460
+ gap: 6px;
461
+ }
462
+
463
+ .nua-cadmin-array-item {
464
+ display: flex;
465
+ align-items: flex-start;
466
+ gap: 6px;
467
+ }
468
+
469
+ .nua-cadmin-array-item-body {
470
+ flex: 1;
471
+ min-width: 0;
472
+ }
473
+
474
+ .nua-cadmin-icon-btn {
475
+ flex-shrink: 0;
476
+ width: 28px;
477
+ height: 28px;
478
+ border: 1px solid var(--nua-cadmin-border);
479
+ border-radius: 6px;
480
+ background: var(--nua-cadmin-bg);
481
+ color: var(--nua-cadmin-muted);
482
+ cursor: pointer;
483
+ font-size: 15px;
484
+ line-height: 1;
485
+ }
486
+
487
+ .nua-cadmin-icon-btn:hover {
488
+ border-color: var(--nua-cadmin-danger);
489
+ color: var(--nua-cadmin-danger);
490
+ }
491
+
492
+ .nua-cadmin-add-btn {
493
+ align-self: flex-start;
494
+ padding: 5px 10px;
495
+ border: 1px dashed var(--nua-cadmin-border);
496
+ border-radius: 6px;
497
+ background: var(--nua-cadmin-bg);
498
+ color: var(--nua-cadmin-accent);
499
+ font-size: 12px;
500
+ cursor: pointer;
501
+ }
502
+
503
+ .nua-cadmin-add-btn:hover:not(:disabled) {
504
+ border-color: var(--nua-cadmin-accent);
505
+ background: var(--nua-cadmin-bg-subtle);
506
+ }
507
+
508
+ .nua-cadmin-add-btn:disabled {
509
+ opacity: 0.5;
510
+ cursor: default;
511
+ }
512
+
513
+ /* --- Nested object group --- */
514
+
515
+ .nua-cadmin-object {
516
+ display: flex;
517
+ flex-direction: column;
518
+ gap: 8px;
519
+ padding: 8px 10px;
520
+ border: 1px solid var(--nua-cadmin-border);
521
+ border-radius: 8px;
522
+ background: var(--nua-cadmin-bg-subtle);
523
+ }
524
+
525
+ .nua-cadmin-object-field {
526
+ display: flex;
527
+ flex-direction: column;
528
+ gap: 3px;
529
+ }
530
+
531
+ /* --- Media picker --- */
532
+
533
+ .nua-cadmin-media {
534
+ display: flex;
535
+ flex-direction: column;
536
+ gap: 6px;
537
+ }
538
+
539
+ .nua-cadmin-media-row {
540
+ display: flex;
541
+ align-items: center;
542
+ gap: 6px;
543
+ }
544
+
545
+ .nua-cadmin-media-hint {
546
+ color: var(--nua-cadmin-muted);
547
+ font-size: 12px;
548
+ font-style: italic;
549
+ }
550
+
551
+ .nua-cadmin-media-error {
552
+ color: var(--nua-cadmin-danger);
553
+ font-size: 12px;
554
+ }
555
+
556
+ .nua-cadmin-file-input {
557
+ display: none;
558
+ }
559
+
560
+ /* --- Editor layout --- */
561
+
562
+ .nua-cadmin-editor {
563
+ display: flex;
564
+ flex-direction: column;
565
+ gap: 12px;
566
+ }
567
+
568
+ .nua-cadmin-editor-toolbar {
569
+ display: flex;
570
+ align-items: center;
571
+ gap: 8px;
572
+ }
573
+
574
+ .nua-cadmin-entries-toolbar {
575
+ display: flex;
576
+ justify-content: flex-end;
577
+ margin-bottom: 10px;
578
+ }
579
+
580
+ .nua-cadmin-editor-header-fields {
581
+ display: flex;
582
+ flex-direction: column;
583
+ gap: 10px;
584
+ padding-bottom: 12px;
585
+ border-bottom: 1px solid var(--nua-cadmin-border);
586
+ }
587
+
588
+ .nua-cadmin-editor-grid {
589
+ display: grid;
590
+ grid-template-columns: 1fr;
591
+ gap: 18px;
592
+ }
593
+
594
+ @media (min-width: 720px) {
595
+ .nua-cadmin-editor-grid:has(.nua-cadmin-editor-sidebar) {
596
+ grid-template-columns: minmax(0, 1fr) 240px;
597
+ }
598
+ }
599
+
600
+ .nua-cadmin-editor-main,
601
+ .nua-cadmin-editor-sidebar {
602
+ display: flex;
603
+ flex-direction: column;
604
+ gap: 12px;
605
+ min-width: 0;
606
+ }
607
+
608
+ .nua-cadmin-editor-sidebar {
609
+ padding: 12px;
610
+ border: 1px solid var(--nua-cadmin-border);
611
+ border-radius: 8px;
612
+ background: var(--nua-cadmin-bg-subtle);
613
+ align-self: start;
614
+ }
615
+
616
+ .nua-cadmin-field-publish-toggle,
617
+ .nua-cadmin-field-publish-date {
618
+ padding-bottom: 10px;
619
+ border-bottom: 1px solid var(--nua-cadmin-border);
620
+ }
621
+
622
+ .nua-cadmin-group-title {
623
+ margin: 8px 0 2px;
624
+ font-size: 11px;
625
+ font-weight: 700;
626
+ text-transform: uppercase;
627
+ letter-spacing: 0.04em;
628
+ color: var(--nua-cadmin-muted);
629
+ }
630
+
631
+ /* --- Buttons --- */
632
+
633
+ .nua-cadmin-btn {
634
+ padding: 6px 12px;
635
+ border: 1px solid var(--nua-cadmin-border);
636
+ border-radius: 6px;
637
+ background: var(--nua-cadmin-bg);
638
+ color: var(--nua-cadmin-fg);
639
+ font-size: 12px;
640
+ font-weight: 500;
641
+ cursor: pointer;
642
+ }
643
+
644
+ .nua-cadmin-btn:hover:not(:disabled) {
645
+ background: var(--nua-cadmin-bg-subtle);
646
+ }
647
+
648
+ .nua-cadmin-btn:disabled {
649
+ opacity: 0.5;
650
+ cursor: default;
651
+ }
652
+
653
+ .nua-cadmin-btn-primary {
654
+ border-color: var(--nua-cadmin-accent);
655
+ background: var(--nua-cadmin-accent);
656
+ color: #ffffff;
657
+ }
658
+
659
+ .nua-cadmin-btn-primary:hover:not(:disabled) {
660
+ background: #1d4ed8;
661
+ }
662
+
663
+ .nua-cadmin-btn-danger {
664
+ border-color: #fecaca;
665
+ color: var(--nua-cadmin-danger);
666
+ }
667
+
668
+ .nua-cadmin-btn-danger:hover:not(:disabled) {
669
+ background: var(--nua-cadmin-danger-bg);
670
+ }
671
+
672
+ .nua-cadmin-btn-ghost {
673
+ border-color: transparent;
674
+ background: transparent;
675
+ color: var(--nua-cadmin-muted);
676
+ }
677
+
678
+ .nua-cadmin-btn-ghost:hover:not(:disabled) {
679
+ background: var(--nua-cadmin-bg-subtle);
680
+ color: var(--nua-cadmin-fg);
681
+ }
682
+
683
+ /* --- Save status badge --- */
684
+
685
+ .nua-cadmin-status {
686
+ display: inline-flex;
687
+ align-items: center;
688
+ padding: 3px 9px;
689
+ border-radius: 999px;
690
+ font-size: 11px;
691
+ font-weight: 600;
692
+ }
693
+
694
+ .nua-cadmin-status-saving {
695
+ background: var(--nua-cadmin-bg-subtle);
696
+ color: var(--nua-cadmin-muted);
697
+ }
698
+
699
+ .nua-cadmin-status-saved {
700
+ background: #ecfdf5;
701
+ color: #166534;
702
+ }
703
+
704
+ .nua-cadmin-status-conflict {
705
+ background: #fffbeb;
706
+ color: #92400e;
707
+ }
708
+
709
+ .nua-cadmin-status-error {
710
+ background: var(--nua-cadmin-danger-bg);
711
+ color: var(--nua-cadmin-danger);
712
+ }
713
+
714
+ /* --- Conflict dialog --- */
715
+
716
+ .nua-cadmin-dialog-backdrop {
717
+ position: absolute;
718
+ inset: 0;
719
+ display: flex;
720
+ align-items: center;
721
+ justify-content: center;
722
+ padding: 24px;
723
+ background: rgba(17, 24, 39, 0.4);
724
+ z-index: 10;
725
+ }
726
+
727
+ .nua-cadmin-dialog {
728
+ width: 100%;
729
+ max-width: 420px;
730
+ padding: 18px;
731
+ border-radius: 10px;
732
+ background: var(--nua-cadmin-bg);
733
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
734
+ }
735
+
736
+ .nua-cadmin-dialog-title {
737
+ font-size: 14px;
738
+ font-weight: 700;
739
+ margin-bottom: 8px;
740
+ }
741
+
742
+ .nua-cadmin-dialog-body {
743
+ color: var(--nua-cadmin-muted);
744
+ margin-bottom: 16px;
745
+ }
746
+
747
+ .nua-cadmin-dialog-actions {
748
+ display: flex;
749
+ flex-wrap: wrap;
750
+ gap: 8px;
751
+ justify-content: flex-end;
752
+ }
@@ -1,88 +0,0 @@
1
- /**
2
- * Read-only field rendering, driven by a collection's `FieldDefinition`.
3
- *
4
- * Scalar types (text/number/boolean/date/select/image/url/email/tel/color) show
5
- * their value directly; structural types (array/object/reference) show their
6
- * structure. Nothing here mutates — editing arrives in F3.2.
7
- */
8
-
9
- import type { FieldDefinition, FieldType } from '@nuasite/cms-types'
10
-
11
- /**
12
- * Frontmatter values arrive from the sidecar already stringified
13
- * (`{ value: string; line: number }`). For structural types the string is a
14
- * JSON payload; we parse it best-effort for a structured render and fall back to
15
- * the raw string when it is not JSON.
16
- */
17
- function parseStructured(raw: string): unknown {
18
- const trimmed = raw.trim()
19
- if (trimmed === '') return undefined
20
- if (!(trimmed.startsWith('{') || trimmed.startsWith('['))) return raw
21
- try {
22
- return JSON.parse(trimmed)
23
- } catch {
24
- return raw
25
- }
26
- }
27
-
28
- function StructuredValue({ raw }: { raw: string }) {
29
- const parsed = parseStructured(raw)
30
- if (parsed === undefined) {
31
- return <span className="nua-cadmin-field-empty">—</span>
32
- }
33
- if (typeof parsed === 'string') {
34
- return <span>{parsed}</span>
35
- }
36
- return <pre className="nua-cadmin-field-structured">{JSON.stringify(parsed, null, 2)}</pre>
37
- }
38
-
39
- function BooleanValue({ raw }: { raw: string }) {
40
- const on = raw === 'true' || raw === '1' || raw.toLowerCase() === 'yes'
41
- return <span className={on ? 'nua-cadmin-bool-on' : 'nua-cadmin-bool-off'}>{on ? 'Yes' : 'No'}</span>
42
- }
43
-
44
- function ImageValue({ raw }: { raw: string }) {
45
- if (raw === '') return <span className="nua-cadmin-field-empty">—</span>
46
- const looksLikeUrl = /^(https?:\/\/|\/)/.test(raw)
47
- return (
48
- <div>
49
- {looksLikeUrl ? <img className="nua-cadmin-img" src={raw} alt="" /> : null}
50
- <div className="nua-cadmin-cell-mono">{raw}</div>
51
- </div>
52
- )
53
- }
54
-
55
- const STRUCTURAL_TYPES: ReadonlySet<FieldType> = new Set<FieldType>(['array', 'object', 'reference'])
56
-
57
- export function FieldValueView({ field, raw }: { field: FieldDefinition; raw: string | undefined }) {
58
- if (raw === undefined || raw === '') {
59
- return <div className="nua-cadmin-field-value nua-cadmin-field-empty">—</div>
60
- }
61
-
62
- let content: React.ReactNode
63
- switch (field.type) {
64
- case 'boolean':
65
- content = <BooleanValue raw={raw} />
66
- break
67
- case 'image':
68
- case 'file':
69
- content = <ImageValue raw={raw} />
70
- break
71
- default:
72
- content = STRUCTURAL_TYPES.has(field.type) ? <StructuredValue raw={raw} /> : <span>{raw}</span>
73
- }
74
-
75
- return <div className="nua-cadmin-field-value">{content}</div>
76
- }
77
-
78
- export function FieldRow({ field, raw }: { field: FieldDefinition; raw: string | undefined }) {
79
- return (
80
- <div className="nua-cadmin-field">
81
- <div className="nua-cadmin-field-label">
82
- <span>{field.name}</span>
83
- <span className="nua-cadmin-field-type">{field.type}{field.required ? ' · required' : ''}</span>
84
- </div>
85
- <FieldValueView field={field} raw={raw} />
86
- </div>
87
- )
88
- }